L8 SemaphoreMonitor v2
L8 SemaphoreMonitor v2
http://www.cs.princeton.edu/courses/archive/fall11/cos318/
Today’s Topics
Semaphores
Monitors
Mesa-style monitors
Programming idiom
2
Mutual Exclusion and Critical Sections
A critical section is a piece of code in which a process or
thread accesses a common (shared or global) resource.
3
Conditions for a good Mutex solution:
4
The Big Picture
High-Level
Mutex Semaphores Monitors Send/Recv
Atomic API
Interrupts CPU
Multiprocessors
(I/O, timer) scheduling
5
Semaphores (Dijkstra, 1965)
Initialization
Initialize a value atomically
P (or Down or Wait) definition
Atomic operation
Wait for semaphore to become positive and then decrement
P(s){
while (s <= 0)
;
Analogy: Think about semaphore
s--; value as the number of empty
} chairs at a table…
V (or Up or Signal) definition
Atomic operation
Increment semaphore by 1
V(s){ The atomicity and the waiting
s++;
}
can be implemented by either
busywaiting or blocking
solutions. 6
An aside on Dijkstra…
Quite a personality…Avoided owning a computer for
several decades into his career…Won the 1972 Turing
Award…
Created a series of numbered memos with his thoughts
on computing topics
Now Archived at U. Texas:
http://www.cs.utexas.edu/~EWD/
Example: “A Tutorial on the Split Binary Semaphore”
• http://www.cs.utexas.edu/~EWD/ewd07xx/EWD703.PDF
Some are short proofs or papers, others are jokes or rants.
Go-to statement considered harmful: Published in CACM
1968, also as EWD215…
7
Semaphores can be used for…
Binary semaphores can provide mutual exclusion
(solution of critical section problem)
Counting semaphores can represent a resource with
multiple instances (e.g. solving producer/consumer
problem)
Signaling events (persistent events that stay
relevant even if nobody listening right now)
Classic Synchronization Problems
There are a number of “classic” problems that represent
a class of synchronization situations
Critical Section problem
Producer/Consumer problem
Reader/Writer problem
5 Dining Philosophers
V(emptybuf);
fill empty buffer with item
V(fullbuf); use item
}
}
Semaphores: emptybuf initially N; fullbuf initially 0;
Producer Consumer (Bounded Buffer)
with Semaphores: More detail…
producer() { consumer() {
while (1) { while (1) {
produce an item P(fullBuf);
P(emptyBuf);
P(mutex);
P(mutex); take an item from buffer
put the item in buffer V(mutex);
V(mutex);
V(emptyBuf);
V(fullBuf); consume the item
} }
} }
Init:
emptyCount = N; fullCount = 0; mutex = 1
Are P(mutex)and V(mutex) necessary?
Example: Interrupt Handler
A device thread works with an interrupt handler
What to do with shared data?
... ...
?
Acquire(m); Acquire(m);
... ...
Release(m); Release(m);
... ...
13
Use Semaphore to Signal
Init(s,0);
14
Semaphores Are Not Always Convenient
Mesa-style monitors
Programming idiom
Barriers
16
The Big Picture
High-Level
Mutex Semaphores Monitors Send/Recv
Atomic API
Interrupts CPU
Multiprocessors
(I/O, timer) scheduling
17
Monitor: Hide Mutual Exclusion
Shared
data
Queue of waiting processes
trying to enter the monitor
...
procedures
Condition Variables in A Monitor
...
Entry queue
procedures
Monitor Abstraction
Encapsulates shared
notEmpty
entry queue
data and operations
with mutual exclusive monitor_lock
use of the object (an
associated lock).
enQ deQ
Associated Condition
Variables with
init
operations of Wait shared data
and Signal.
conditions
Condition Variables
EnQ:{acquire (lock);
if (head == null)
notEmpty
entry queue
{head = item;
signal (lock, notEmpty);}
else tail->next = item; monitor_lock
tail = item;
release(lock);}
deQ:{acquire (lock); enQ deQ
if (head == null)
wait (lock, notEmpty);
init
item = head; shared data
if (tail == head) tail = null;
head=item->next;
conditions
release(lock);}
Monitor Abstraction
EnQ:{acquire (lock);
if (head == null)
notEmpty
entry queue
{head = item;
signal (lock, notEmpty);}
else tail->next = item; monitor_lock
tail = item;
release(lock);}
deQ:{acquire (lock); enQ deQ
if (head == null)
wait (lock, notEmpty);
init
item = head; shared data
if (tail == head) tail = null;
head=item->next;
conditions
release(lock);}
Monitor Abstraction
EnQ:{acquire (lock);
if (head == null)
notEmpty
entry queue
{head = item;
signal (lock, notEmpty);}
else tail->next = item; monitor_lock
tail = item;
release(lock);}
deQ:{acquire (lock); enQ deQ
if (head == null)
wait (lock, notEmpty);
init
item = head; shared data
if (tail == head) tail = null;
head=item->next;
conditions
release(lock);}
Monitor Abstraction
EnQ:{acquire (lock);
if (head == null)
notEmpty
entry queue
{head = item;
signal (lock, notEmpty);}
else tail->next = item; monitor_lock
tail = item;
release(lock);}
deQ:{acquire (lock); enQ deQ
if (head == null)
wait (lock, notEmpty);
init
item = head; shared data
if (tail == head) tail = null;
head=item->next;
conditions
release(lock);}
Monitor Abstraction
EnQ:{acquire (lock);
if (head == null)
notEmpty
entry queue
{head = item;
signal (lock, notEmpty);}
else tail->next = item; monitor_lock
tail = item;
release(lock);}
deQ:{acquire (lock); enQ deQ
if (head == null)
wait (lock, notEmpty);
init
item = head; shared data
if (tail == head) tail = null;
head=item->next;
conditions
release(lock);}
Monitor Abstraction
EnQ:{acquire (lock);
if (head == null)
notEmpty
entry queue
{head = item;
signal (lock, notEmpty);}
else tail->next = item; monitor_lock
tail = item;
release(lock);}
deQ:{acquire (lock); enQ deQ
while (head == null)
wait (lock, notEmpty);
init
item = head; shared data
if (tail == head) tail = null;
head=item->next;
conditions
release(lock);}
Producer-Consumer with Monitors
monitor ProdCons
condition full, empty;
procedure Producer
begin procedure Enter;
while true do begin
begin if (buffer is full)
produce an item wait(full);
ProdCons.Enter(); put item into buffer;
end; if (only one item)
end; signal(empty);
end;
procedure Consumer
begin procedure Remove;
while true do begin
begin if (buffer is empty)
ProdCons.Remove(); wait(empty);
consume an item; remove an item;
end; if (buffer was full)
end; signal(full);
end;
Options of the Signaler
Run the signaled thread immediately and suspend the
current one (Hoare)
If the signaler has other work to do, life is complex
It is difficult to make sure there is nothing to do, because the
signal implementation is not aware of how it is used
It is easy to prove things
Exit the monitor (Hansen)
Signal must be the last statement of a monitor procedure
Continues its execution (Mesa)
Easy to implement
But, the condition may not be true when the awaken process
actually gets a chance to run
29
Today’s Topics
Semaphores
Monitors
Mesa-style monitors
Programming idiom
Barriers
30
Mesa Style “Monitor” (Birrell’s Paper)
Associate a condition variable with a mutex
Wait( mutex, condition )
Atomically unlock the mutex and enqueued on the condition
variable (block the thread)
Re-lock the lock when it is awakened
Signal( condition )
No-op if there is no thread blocked on the condition variable
Wake up at least one if there are threads blocked
Broadcast( condition )
Wake up all waiting threads
Original Mesa paper
B. Lampson and D. Redell. Experience with processes and
monitors in Mesa. Comm. ACM 23, 2 (feb 1980), pp 106-117.
Consumer-Producer with Mesa-Style Monitor
static count = 0;
static Cond full, empty;
static Mutex lock;
static count = 0;
static Cond full, empty;
static Mutex lock;
33
Today’s Topics
Semaphores
Monitors
Mesa-style monitors
Programming idiom
Barriers
34
The Programming Idiom
Waiting for a resource Make a resource available
35
Revisit the Motivation Example
Waking up:
• Acquire mutex
• Resume
37
More on Mesa-Style Monitor
Signaler continues execution
Waiters simply put on ready queue, with no special
priority
Must reevaluate the condition
No constraints on when the waiting thread/process must
run after a “signal”
Simple to introduce a broadcast: wake up all
No constrains on signaler
Can execute after signal call (Hansen’s cannot)
Do not need to relinquish control to awaken thread/process
Evolution of Monitors
Brinch-Hansen (73) and Hoare Monitor (74)
Concept, but no implementation
Requires Signal to be the last statement (Hansen)
Requires relinquishing CPU to signaler (Hoare)
Mesa Language (77)
Monitor in language, but signaler keeps mutex and CPU
Waiter simply put on ready queue, with no special priority
Modula-2+ (84) and Modula-3 (88)
Explicit LOCK primitive
Mesa-style monitor
Pthreads (95)
Started standard effort around 1989
Defined by ANSI/IEEE POSIX 1003.1 Runtime library
Java threads
Use ‘synchronized’ primitive for mutual exclusion
Wait() and notify() use implicit per-class condition variable
Today’s Topics
Semaphores
Monitors
Mesa-style monitors
Programming idiom
Barriers
40
Example: A Simple Barrier
Thread A and Thread B
want to meet at a Thread A Thread B
particular point and then
go on
41
Using Semaphores as A Barrier
Use two semaphore?
init(s1, 0);
init(s2, 0);
Thread A Thread B
… …
V(s1); V(s2);
P(s2); P(s1);
… …
42
Barrier Primitive
Functions
Take a barrier variable
Broadcast to n-1 threads Thread 1 Thread n
… ... …
When barrier variable has Barrier(b); Barrier(b);
reached n, go forward … …
Hardware support on
some parallel machines
Barrier
variable
43
Equivalence
Semaphores
Good for signaling
Not good for mutex because it is easy to introduce a bug
Monitors
Good for scheduling and mutex
Maybe costly for a simple signaling
44
Summary
Semaphores
Monitors
Barriers
45
5 Dining Philosophers
Philosopher 1
Philosopher 3
Philosopher 2
Template for Philosopher
eat;
/*put down forks*/
think awhile;
}
Naive Solution
eat;
/*put down forks*/
V(fork[left(me)]);
V(fork[right(me)]);
think awhile;
} Does this work?
Simplest Example of Deadlock
Circular waiting
Thread 0 waits for Thread 1 to V(R2) and
Thread 1 waits for Thread 0 to V(R1)
Hold and wait
Holding either R1 or R2 while waiting on other
No pre-emption
Neither R1 nor R2 are removed from their respective holding
Threads.
Philosophy 101
(or why 5DP is interesting)
think awhile;
}
Hold and Wait Condition
Producer/Consumer problem
Reader/Writer problem
5 Dining Philosophers