Operating Systems Semaphores II. Producer/Consumer Problem Consumer must wait for producer to fill...

22
Operating Systems Semaphores II

Transcript of Operating Systems Semaphores II. Producer/Consumer Problem Consumer must wait for producer to fill...

Page 1: Operating Systems Semaphores II. Producer/Consumer Problem Consumer must wait for producer to fill buffers, if none full –(scheduling constraint) Producer.

Operating Systems Semaphores II

Page 2: Operating Systems Semaphores II. Producer/Consumer Problem Consumer must wait for producer to fill buffers, if none full –(scheduling constraint) Producer.

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

Page 3: Operating Systems Semaphores II. Producer/Consumer Problem Consumer must wait for producer to fill buffers, if none full –(scheduling constraint) Producer.

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); }}

Page 4: Operating Systems Semaphores II. Producer/Consumer Problem Consumer must wait for producer to fill buffers, if none full –(scheduling constraint) Producer.

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

Page 5: Operating Systems Semaphores II. Producer/Consumer Problem Consumer must wait for producer to fill buffers, if none full –(scheduling constraint) Producer.

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

Page 6: Operating Systems Semaphores II. Producer/Consumer Problem Consumer must wait for producer to fill buffers, if none full –(scheduling constraint) Producer.

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

Page 7: Operating Systems Semaphores II. Producer/Consumer Problem Consumer must wait for producer to fill buffers, if none full –(scheduling constraint) Producer.

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

Page 8: Operating Systems Semaphores II. Producer/Consumer Problem Consumer must wait for producer to fill buffers, if none full –(scheduling constraint) Producer.

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

Page 9: Operating Systems Semaphores II. Producer/Consumer Problem Consumer must wait for producer to fill buffers, if none full –(scheduling constraint) Producer.

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?

Page 10: Operating Systems Semaphores II. Producer/Consumer Problem Consumer must wait for producer to fill buffers, if none full –(scheduling constraint) Producer.

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();

Page 11: Operating Systems Semaphores II. Producer/Consumer Problem Consumer must wait for producer to fill buffers, if none full –(scheduling constraint) Producer.

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

Page 12: Operating Systems Semaphores II. Producer/Consumer Problem Consumer must wait for producer to fill buffers, if none full –(scheduling constraint) Producer.

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.

Page 13: Operating Systems Semaphores II. Producer/Consumer Problem Consumer must wait for producer to fill buffers, if none full –(scheduling constraint) Producer.

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.

Page 14: Operating Systems Semaphores II. Producer/Consumer Problem Consumer must wait for producer to fill buffers, if none full –(scheduling constraint) Producer.

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.

Page 15: Operating Systems Semaphores II. Producer/Consumer Problem Consumer must wait for producer to fill buffers, if none full –(scheduling constraint) Producer.

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

Page 16: Operating Systems Semaphores II. Producer/Consumer Problem Consumer must wait for producer to fill buffers, if none full –(scheduling constraint) Producer.

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.

Page 17: Operating Systems Semaphores II. Producer/Consumer Problem Consumer must wait for producer to fill buffers, if none full –(scheduling constraint) Producer.

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.

Page 18: Operating Systems Semaphores II. Producer/Consumer Problem Consumer must wait for producer to fill buffers, if none full –(scheduling constraint) Producer.

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;}

Page 19: Operating Systems Semaphores II. Producer/Consumer Problem Consumer must wait for producer to fill buffers, if none full –(scheduling constraint) Producer.

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.

Page 20: Operating Systems Semaphores II. Producer/Consumer Problem Consumer must wait for producer to fill buffers, if none full –(scheduling constraint) Producer.

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.

Page 21: Operating Systems Semaphores II. Producer/Consumer Problem Consumer must wait for producer to fill buffers, if none full –(scheduling constraint) Producer.

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.

Page 22: Operating Systems Semaphores II. Producer/Consumer Problem Consumer must wait for producer to fill buffers, if none full –(scheduling constraint) Producer.

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);}}