Operating Systems Semaphores II
Producer/Consumer Problem• Consumer must wait for producer to fill buffers,
if none full– (scheduling constraint)
• Producer must wait for consumer to empty buffers, if all full– (scheduling constraint)
• Only one thread can manipulate buffer queue at a time– (mutual exclusion)
Use a separate semaphore for each constraint
Scheduling
Constraint 1
Scheduling Constraint 2
Mutual Exclusion
P() is a Generalization of Sleep()
V() is a Generalization of Wakeup()
int BUFFER_SIZE = 100; int count = 0; void producer(void) { int item;
while(TRUE) {produce_item(&item);if(count == BUFFER_SIZE)
sleep ();enter_item(item);count++;if(count == 1)
wakeup(consumer);}}
void consumer(void) { int item;while(TRUE) {
if(count == 0) sleep ();
remove_item(&item);count--;if(count == BUFFER_SIZE - 1)
wakeup(producer);consume_item(&item); }}
int BUFFER_SIZE = 100; int count = 0; void producer(void) { int item;
while(TRUE) {produce_item(&item);if(count == BUFFER_SIZE)
sleep ();
enter_item(item);if(count == 1)
wakeup(consumer);}}
void consumer(void) { int item;while(TRUE) {
if(count == 0) sleep ();
remove_item(&item);if(count == BUFFER_SIZE - 1)
wakeup(producer);consume_item(&item); }}
Scheduling
Constraint 1
Scheduling Constraint 2
Mutual Exclusion
P() is a Generalization of Sleep()
V() is a Generalization of Wakeup()
Sleep if buffer full
Wake up if space in buffer
Wake up if first item in buffer
Sleep if buffer empty
Get Exclusive Access of Queue
Leave Exclusive Access of Queue
Get Exclusive Access of Queue
Leave Exclusive Access of Queue
int BUFFER_SIZE = 100; int count = 0; void producer(void) { int item;
while(TRUE) {produce_item(&item);empty->P();CountMutex->P();
enter_item(item);CountMutex->V();full->V();
}} void consumer(void) { int item;
while(TRUE) {full->P();CountMutex->P();
remove_item(&item);CountMutex->V();empty->V();consume_item(&item); }}
Scheduling
Constraint 1
Scheduling Constraint 2
Mutual Exclusion
Semaphore Empty(BUFFER_SIZE);Semaphore full(0);Semaphore CountMutex(1);
Sleep if buffer full
Wake up if space in buffer
Wake up if first item in buffer
Sleep if buffer empty
Get Exclusive Access of Queue
Leave Exclusive Access of Queue
Get Exclusive Access of Queue
Leave Exclusive Access of Queue
Producer/Consumer Problem
solved with Semaphores
Semaphore API
• #include <semaphore.h>• How to declare semaphore?• sem_t S;• Initialization…• sem_init(&S,0,1);• Decrement ( P())• sem_wait(&S);• Increment ( V() )• sem_post(&S);
Max Value
Example - Ping Pong…
• Suppose we have two threads A and B• A and B are to repeatedly print out ping and pong,
respectively • We want to execute them in an alternating order• An alternating execution would force A and B to
print out in the order of – ping pong ping pong ping pong
• Write the pseudocode of the thread A and B• How can this be solved with and without
semaphores
Example - Threads• A Thread creates 5 Threads. Thread 1 is created first,
Thread 2 is created second, Thread 3 is created third and so on. Assume FIFO to be the scheduling policy. Each thread prints the sequence number of its creation. Write the code that will print exactly the following:
• I am Thread 5, I was created at number 5• I am Thread 3, I was created at number 3• I am Thread 4, I was created at number 4• I am Thread 1, I was created at number 1• I am Thread 2, I was created at number 2
Example …
• A thread may delay its execution by calling a function
• Void Delay(int second)• The thread itself is blocked (no busy waiting by
this thread)• Wakesup roughly after second• Hint I: A new thread is spawned whenever delay
is called• Hint II: Loops for delay?
int BUFFER_SIZE = 100; int count = 0; void producer(void) { int item;
while(TRUE) {produce_item(&item);empty->P();CountMutex->P();
enter_item(item);CountMutex->V();full->V();
}} void consumer(void) { int item;
while(TRUE) {full->P();CountMutex->P();
remove_item(&item);CountMutex->V();empty->V();consume_item(&item);
}}
Semaphore Empty(BUFFER_SIZE);Semaphore full(0);Semaphore CountMutex(1);
What will happen if we swapempty->P();
CountMutex->P();
int BUFFER_SIZE = 100; int count = 0; void producer(void) { int item;
while(TRUE) {produce_item(&item);CountMutex->P();empty->P();
enter_item(item);CountMutex->V();full->V();
}} void consumer(void) { int item;
while(TRUE) {full->P();CountMutex->P();
remove_item(&item);CountMutex->V();empty->V();consume_item(&item);
}}
Semaphore Empty(BUFFER_SIZE);Semaphore full(0);Semaphore CountMutex(1);
Suppose the buffer is fullWhere is the producer???Where is the consumer???
DEADLOCKDEADLOCKDEADLOCKDEADLOCK
Solution• One has to be really careful while working with the
Semaphores • Problem with semaphores:
– Used for both mutex
– and scheduling constraints. • This makes the code hard to read, and hard to get
right.• Monitor: A higher level synchronization primitive• Separate these 2 concepts:
– use locks for mutual exclusion– condition variables for scheduling constraints.
Monitor
• A Monitor consists of– A lock– Zero or more condition variables
• For managing concurrent access to shared data
• Only one process can be active in the monitor at a time
• Acquire the lock on entry and Release the lock before returning.
• With condition variables, the module methods may wait and signal on multiple independent conditions.
Locks: A simple exampleAddToQueue() {
lock.Acquire(); // lock before using shared data
put item on queue; // ok to access shared data
lock.Release(); // unlock after done with shared data
}
RemoveFromQueue() {
lock.Acquire(); // lock before using shared data
if something on queue // ok to access shared data
remove it;
lock.Release(); // unlock after done with shared data
return item;
}
• “Roughly Equivalent” to a semaphore with value 1– Restriction that the thread that “locks” must be the one that
unlocks it.
A simple example
• How do we change RemoveFromQueue() to wait until something is on the queue?
• Logically, want to go to sleep inside of critical section
• But if hold lock when go to sleep?– other threads won't be able to get in to add
things to the queue– And then wake up the sleeping thread
Condition Variables:
• Sleep inside critical section, by atomically releasing lock at same time we go to sleep
• Condition variable: – A queue of threads – Waiting for something inside a critical section.
Condition Variables: Support three operations
• Wait()– release lock, go to sleep, re-acquire lock
• Releasing lock and going to sleep is atomic
• Signal()– wake up a waiter, if any
• Broadcast()– wake up all waiters
• Rule: must hold lock when doing condition variable operations.
A simple exampleAddToQueue() {lock.Acquire(); // lock before using shared
// dataput item on queue; // ok to access shared datacondition.signal();lock.Release(); // unlock after done with
// shared data}RemoveFromQueue() {lock.Acquire();while nothing on queue
condition.wait(&lock);// release lock; go // to sleep; // re-acquire lock
remove item from queue;lock.Release();return item;}
How is a conditional variable different from Semaphore?
• What if thread calls V() and no one is waiting?– Increment – Wakeup is saved.
• What if thread later does calls P()? – Decrement and continue – Wakeup is used.
• P + V are commutative – Result is the same no matter what order they occur.
• What if thread signals and no one is waiting?– Nothing Happens – No Wakeups are saved.
• What if thread later waits? – Thread waits.
How is a conditional variable different from Semaphore?
• Conditional Variables are not counters• Just unblock a thread if one is blocked on the
conditional variable• No counter incremented/decremented• Just like Sleep/Wakeup except that this is atomic• That's why they must be accessed in a critical
section• Before waiting, must check the state variables.
Producer/Consumer Problem with Monitor
• Constraints– 1. Producers can produce if buffer not full
• Condition: okToProduce
– 2. Consumers can consume if buffer not empty
• Condition: okToConsume
– 3. Only one thread manipulates state variables at a time.
int empty = BUFFER_SIZE; void producer(void) { int item;
while(TRUE) {produce_item(&item);mutex->lock();if(empty < = 0)
okToProduce->Wait(&mutex);enter_item(item); empty--;okToConsume->Signal();mutex->unlock();}}
void consumer(void) { int item;while(TRUE) {
mutex->lock();if(empty >= BUFFER_SIZE)
okToConsume->Wait(&mutex);remove_item(&item); empty++;okToProduce->Signal();mutex->unlock();consume_item(&item);}}