Download - Condition Variables
![Page 1: Condition Variables](https://reader035.vdocuments.net/reader035/viewer/2022062305/568164aa550346895dd6a729/html5/thumbnails/1.jpg)
1
Condition Variables
CS 241Prof. Brighten GodfreyMarch 16, 2012University of Illinois
![Page 2: Condition Variables](https://reader035.vdocuments.net/reader035/viewer/2022062305/568164aa550346895dd6a729/html5/thumbnails/2.jpg)
2
Synchronization primitives
Mutex locks• Used for exclusive access to a shared resource (critical
section)• Operations: Lock, unlock
Sempahores• Generalization of mutexes: Count number of available
“resources”• Wait for an available resource (decrement), notify
availability (increment)• Example: wait for free buffer space, signal more buffer
space
Condition variables• Represent an arbitrary event• Operations: Wait for event, signal occurrence of event• Tied to a mutex for mutual exclusion
![Page 3: Condition Variables](https://reader035.vdocuments.net/reader035/viewer/2022062305/568164aa550346895dd6a729/html5/thumbnails/3.jpg)
3
Condition variables
Goal: Wait for a specific event to happen• Event depends on state shared with multiple threads
Solution: condition variables• “Names” an event• Internally, is a queue of threads waiting for the event
Basic operations• Wait for event• Signal occurrence of event to one waiting thread• Signal occurrence of event to all waiting threads
Signaling, not mutual exclusion• Condition variable is intimately tied to a mutex
![Page 4: Condition Variables](https://reader035.vdocuments.net/reader035/viewer/2022062305/568164aa550346895dd6a729/html5/thumbnails/4.jpg)
4
cond_wait
Assumption• Called with mutex locked by calling thread
Action• Atomically releases mutex, and...• ...blocks thread until condition is next signaled (past
signal not “queued”) or maybe only until some interruption occurs
After return• mutex is already locked again
int pthread_cond_wait(pthread_cond_t * cond, pthread_mutex_t * mutex);
![Page 5: Condition Variables](https://reader035.vdocuments.net/reader035/viewer/2022062305/568164aa550346895dd6a729/html5/thumbnails/5.jpg)
5
cond_signal
Action• Unblocks at least one blocked thread waiting on signal
Note: “Mesa semantics” described here• “Hoare semantics” different• pthreads uses Mesa
int pthread_cond_signal(pthread_cond_t * cond);
![Page 6: Condition Variables](https://reader035.vdocuments.net/reader035/viewer/2022062305/568164aa550346895dd6a729/html5/thumbnails/6.jpg)
6
cond_broadcast
Action• Unblocks all blocked threads waiting on signal
Note: “Mesa semantics” described here• “Hoare semantics” different• pthreads uses Mesa
int pthread_cond_broadcast(pthread_cond_t * cond);
![Page 7: Condition Variables](https://reader035.vdocuments.net/reader035/viewer/2022062305/568164aa550346895dd6a729/html5/thumbnails/7.jpg)
7
Producer-Consumerwith Condition Variables
![Page 8: Condition Variables](https://reader035.vdocuments.net/reader035/viewer/2022062305/568164aa550346895dd6a729/html5/thumbnails/8.jpg)
8
Producer-consumer problemChef (Producer) Waiter (Consumer)
inserts items removes itemsShared resource:bounded buffer
Efficient implementation:circular fixed-size buffer
![Page 9: Condition Variables](https://reader035.vdocuments.net/reader035/viewer/2022062305/568164aa550346895dd6a729/html5/thumbnails/9.jpg)
9
Designing a solutionChef (Producer) Waiter (Consumer)
Wait for empty slotInsert itemSignal item arrival
Wait for item arrivalRemove item
Signal slot available
What synchronization do we need?
![Page 10: Condition Variables](https://reader035.vdocuments.net/reader035/viewer/2022062305/568164aa550346895dd6a729/html5/thumbnails/10.jpg)
10
Designing a solutionChef (Producer) Waiter (Consumer)
Wait for empty slotInsert itemSignal item arrival
Wait for item arrivalRemove item
Signal slot available
What synchronization do we need?
Mutex(shared buffer)
![Page 11: Condition Variables](https://reader035.vdocuments.net/reader035/viewer/2022062305/568164aa550346895dd6a729/html5/thumbnails/11.jpg)
11
Designing a solutionChef (Producer) Waiter (Consumer)
Wait for empty slotInsert itemSignal item arrival
Wait for item arrivalRemove item
Signal empty slot available
What synchronization do we need?
Conditionslot frees up
![Page 12: Condition Variables](https://reader035.vdocuments.net/reader035/viewer/2022062305/568164aa550346895dd6a729/html5/thumbnails/12.jpg)
12
Designing a solutionChef (Producer) Waiter (Consumer)
Wait for empty slotInsert itemSignal item arrival
Wait for item arrivalRemove item
Signal empty slot available
What synchronization do we need?
Conditionitem arrives
![Page 13: Condition Variables](https://reader035.vdocuments.net/reader035/viewer/2022062305/568164aa550346895dd6a729/html5/thumbnails/13.jpg)
13
Producer-Consumer with C.V.’s
/* Global variables */pthread_mutex_t m;pthread_cond_t item_available; /* Event: new item inserted */pthread_cond_t space_available; /* Event: item removed */int items_in_buffer;int max_items; void init(void) { mutex_init(&m, NULL); cond_init(&item_available, NULL); items_in_buffer = 0; max_items = 100;}
(Note: “pthread_” prefix removed from all synchronization calls for compactness)
![Page 14: Condition Variables](https://reader035.vdocuments.net/reader035/viewer/2022062305/568164aa550346895dd6a729/html5/thumbnails/14.jpg)
14
Producer-Consumer with C.V.’svoid consumer(void) { mutex_lock(&m); while (items_in_buffer == 0) cond_wait(&item_available, &m); /* Consume item */ items_in_buffer--; cond_signal(&space_available); mutex_unlock(&m);}
![Page 15: Condition Variables](https://reader035.vdocuments.net/reader035/viewer/2022062305/568164aa550346895dd6a729/html5/thumbnails/15.jpg)
15
Producer-Consumer with C.V.’svoid consumer(void) { mutex_lock(&m); while (items_in_buffer == 0) cond_wait(&item_available, &m); /* Consume item */ items_in_buffer--; cond_signal(&space_available); mutex_unlock(&m);}
void producer(void) { mutex_lock(&m); while (items_in_buffer == max_items) cond_wait(&space_available, &m); /* Produce item */ items_in_buffer++; cond_signal(&item_available); mutex_unlock(&m);}
![Page 16: Condition Variables](https://reader035.vdocuments.net/reader035/viewer/2022062305/568164aa550346895dd6a729/html5/thumbnails/16.jpg)
16
Obvious question #1
“Why does cond_wait() need to know about my mutex?
I’ll just unlock the mutex separately.”
![Page 17: Condition Variables](https://reader035.vdocuments.net/reader035/viewer/2022062305/568164aa550346895dd6a729/html5/thumbnails/17.jpg)
17
void consumer(void) { mutex_lock(&m); while (items_in_buffer == 0) { mutex_unlock(&m); cond_wait(&item_available); mutex_lock(&m); } /* Consume item */ items_in_buffer--; cond_signal(&space_available); mutex_unlock(&m);}
void producer(void) { mutex_lock(&m); ... items_in_buffer++; cond_signal(&item_available); mutex_unlock(&m);}
Condition variable without mutex
![Page 18: Condition Variables](https://reader035.vdocuments.net/reader035/viewer/2022062305/568164aa550346895dd6a729/html5/thumbnails/18.jpg)
18
void consumer(void) { mutex_lock(&m); while (items_in_buffer == 0) { mutex_unlock(&m); cond_wait(&item_available); mutex_lock(&m); } /* Consume item */ items_in_buffer--; cond_signal(&space_available); mutex_unlock(&m);}
void producer(void) { mutex_lock(&m); ... items_in_buffer++; cond_signal(&item_available); mutex_unlock(&m);}
A game of catch
![Page 19: Condition Variables](https://reader035.vdocuments.net/reader035/viewer/2022062305/568164aa550346895dd6a729/html5/thumbnails/19.jpg)
19
void consumer(void) { mutex_lock(&m); while (items_in_buffer == 0) { mutex_unlock(&m); cond_wait(&item_available); mutex_lock(&m); } /* Consume item */ items_in_buffer--; cond_signal(&space_available); mutex_unlock(&m);}
void producer(void) { mutex_lock(&m); ... items_in_buffer++; cond_signal(&item_available); mutex_unlock(&m);}
A game of catch
Problem: Not atomic
After unlock, produceracquires lock,creates condition event,sends signal all beforewait() gets called!
Signal is lost
![Page 20: Condition Variables](https://reader035.vdocuments.net/reader035/viewer/2022062305/568164aa550346895dd6a729/html5/thumbnails/20.jpg)
20
void consumer(void) { mutex_lock(&m); while (items_in_buffer == 0) {
cond_wait(&item_available, &m);
} /* Consume item */ items_in_buffer--; cond_signal(&space_available); mutex_unlock(&m);}
void producer(void) { mutex_lock(&m); ... items_in_buffer++; cond_signal(&item_available); mutex_unlock(&m);}
A game of catch
Solution: atomic
OS guarantees that callingthread will not miss signal
Ties together two actions:Checking if we should waitand Waiting happen whileholding the mutex lock.
![Page 21: Condition Variables](https://reader035.vdocuments.net/reader035/viewer/2022062305/568164aa550346895dd6a729/html5/thumbnails/21.jpg)
21
void consumer(void) { mutex_lock(&m); while (items_in_buffer == 0) cond_wait(&item_available, &m); /* Consume item */ items_in_buffer--; cond_signal(&space_available); mutex_unlock(&m);}
void producer(void) { mutex_lock(&m); ... items_in_buffer++; cond_signal(&item_available); mutex_unlock(&m);}
A successful game of catch
![Page 22: Condition Variables](https://reader035.vdocuments.net/reader035/viewer/2022062305/568164aa550346895dd6a729/html5/thumbnails/22.jpg)
22
Obvious question #2
“Why the while loop?
I’ll just do an if statement.”
...while (items_in_buffer == 0) { cond_wait(&item_available, &m);...
![Page 23: Condition Variables](https://reader035.vdocuments.net/reader035/viewer/2022062305/568164aa550346895dd6a729/html5/thumbnails/23.jpg)
23
No while; just an if?void consumer(void) { mutex_lock(&m); if (items_in_buffer == 0) cond_wait(&item_available, &m); /* Consume item */ items_in_buffer--; cond_signal(&space_available); mutex_unlock(&m);}
void producer(void) { mutex_lock(&m); ... items_in_buffer++; cond_signal(&item_available); mutex_unlock(&m);}
![Page 24: Condition Variables](https://reader035.vdocuments.net/reader035/viewer/2022062305/568164aa550346895dd6a729/html5/thumbnails/24.jpg)
24
No while; just an if?mutex_lock(&m);if (items_in_buffer == 0) cond_wait(&item_available, &m);
/* Consume item */items_in_buffer--;cond_signal(&space_available);mutex_unlock(&m);
T1
mutex_lock(&m);...items_in_buffer++; /* Produce item */cond_signal(&item_available);mutex_unlock(&m);
T2
mutex_lock(&m);if (items_in_buffer == 0) cond_wait(&item_available, &m);/* Consume item */items_in_buffer--;cond_signal(&space_available);mutex_unlock(&m);
T3
Blocked oncondition
Blocked onacquiring mutex(inside cond_wait())
ERROR: Item already consumed!
![Page 25: Condition Variables](https://reader035.vdocuments.net/reader035/viewer/2022062305/568164aa550346895dd6a729/html5/thumbnails/25.jpg)
25
Readers-Writerswith Condition Variables
![Page 26: Condition Variables](https://reader035.vdocuments.net/reader035/viewer/2022062305/568164aa550346895dd6a729/html5/thumbnails/26.jpg)
26
Readers-Writers Problem
Generalization of the mutual exclusion problemProblem statement:
• Reader threads only read the object• Writer threads modify the object• Writers must have exclusive access to the object• Unlimited number of readers can access the object
Reader
Writer
Reader
OK No
Writer No No
Thre
ad 1
Thread 2
![Page 27: Condition Variables](https://reader035.vdocuments.net/reader035/viewer/2022062305/568164aa550346895dd6a729/html5/thumbnails/27.jpg)
27
void writer(void) { while (1) { sem_wait(&w);
/* Critical section */ /* Writing here */
sem_post(&w); }}
Writers:
int readcnt; /* Initially = 0 */sem_t mutex, w; /* Both initially = 1 */
Shared:Recall: Semaphore solution
![Page 28: Condition Variables](https://reader035.vdocuments.net/reader035/viewer/2022062305/568164aa550346895dd6a729/html5/thumbnails/28.jpg)
28
(full codeonline)
void reader(void) { while (1) { sem_wait(&mutex); readcnt++; if (readcnt == 1) /* First reader in */ sem_wait(&w); /* Lock out writers */ sem_post(&mutex);
/* Main critical section */ /* Reading would happen here */
sem_wait(&mutex); readcnt--; if (readcnt == 0) /* Last out */ sem_post(&w); /* Let in writers */ sem_post(&mutex); }}
Readers:Recall: Semaphore solution
![Page 29: Condition Variables](https://reader035.vdocuments.net/reader035/viewer/2022062305/568164aa550346895dd6a729/html5/thumbnails/29.jpg)
29
Condition variable solution
Idea:• If it’s safe, just go ahead and read or write• Otherwise, wait for my “turn”
Initialization:/* Global variables */pthread_mutex_t m;pthread_cond_t turn; /* Event: it's our turn */int writing; int reading; void init(void) { pthread_mutex_init(&m, NULL); pthread_cond_init(&turn, NULL); reading = 0; writing = 0;}
![Page 30: Condition Variables](https://reader035.vdocuments.net/reader035/viewer/2022062305/568164aa550346895dd6a729/html5/thumbnails/30.jpg)
30
Condition variable solution
void reader(void) { mutex_lock(&m); while (writing) cond_wait(&turn, &m); reading++; mutex_unlock(&m);
/* Reading here */
mutex_lock(&m); reading--; cond_signal(&turn); mutex_unlock(&m);}
void writer(void) { mutex_lock(&m); while (reading || writing) cond_wait(&turn, &m); writing++; mutex_unlock(&m);
/* Writing here */
mutex_lock(&m); writing--; cond_signal(&turn); mutex_unlock(&m);}
(Note: “pthread_” prefix removed from all synchronization calls for compactness)
![Page 31: Condition Variables](https://reader035.vdocuments.net/reader035/viewer/2022062305/568164aa550346895dd6a729/html5/thumbnails/31.jpg)
31
Familiar problem: Starvation
void reader(void) { mutex_lock(&m); while (writing) cond_wait(&turn, &m); reading++; mutex_unlock(&m);
/* Reading here */
mutex_lock(&m); reading--; cond_signal(&turn); mutex_unlock(&m);}
void writer(void) { mutex_lock(&m); while (reading || writing) cond_wait(&turn, &m); writing++; mutex_unlock(&m);
/* Writing here */
mutex_lock(&m); writing--; cond_signal(&turn); mutex_unlock(&m);}
(Note: “pthread_” prefix removed from all synchronization calls for compactness)
![Page 32: Condition Variables](https://reader035.vdocuments.net/reader035/viewer/2022062305/568164aa550346895dd6a729/html5/thumbnails/32.jpg)
32
Idea: take turns
If a writer is waiting, then reader should wait its turn
• Even if it’s safe to proceed (only readers are in critical section)
Requires keeping track of waiting writers/* Global variables */pthread_mutex_t m;pthread_cond_t turn; /* Event: someone else's turn */int reading;int writing;int writers; void init(void) { pthread_mutex_init(&m, NULL); pthread_cond_init(&turn, NULL); reading = 0; writing = 0; writers = 0;}
![Page 33: Condition Variables](https://reader035.vdocuments.net/reader035/viewer/2022062305/568164aa550346895dd6a729/html5/thumbnails/33.jpg)
33
Taking turns
void reader(void) { mutex_lock(&m); if (writers) cond_wait(&turn, &m); while (writing) cond_wait(&turn, &m); reading++; mutex_unlock(&m);
/* Reading here */
mutex_lock(&m); reading--; cond_signal(&turn); mutex_unlock(&m);}
void writer(void) { mutex_lock(&m); writers++; while (reading || writing) cond_wait(&turn, &m); writing++; mutex_unlock(&m);
/* Writing here */
mutex_lock(&m); writing--; writers--; cond_signal(&turn); mutex_unlock(&m);}
![Page 34: Condition Variables](https://reader035.vdocuments.net/reader035/viewer/2022062305/568164aa550346895dd6a729/html5/thumbnails/34.jpg)
34
void reader(void) { mutex_lock(&m); if (writers) cond_wait(&turn, &m); while (writing) cond_wait(&turn, &m); reading++; mutex_unlock(&m);
/* Reading here */
mutex_lock(&m); reading--; cond_signal(&turn); mutex_unlock(&m);}
Another problem :-(
void writer(void) { mutex_lock(&m); writers++; while (reading || writing) cond_wait(&turn, &m); writing++; mutex_unlock(&m);
/* Writing here */
mutex_lock(&m); writing--; writers--; cond_signal(&turn); mutex_unlock(&m);}
Only unblocks one thread at a time;Inefficient if many readers are waiting
![Page 35: Condition Variables](https://reader035.vdocuments.net/reader035/viewer/2022062305/568164aa550346895dd6a729/html5/thumbnails/35.jpg)
35
void reader(void) { mutex_lock(&m); if (writers) cond_wait(&turn, &m); while (writing) cond_wait(&turn, &m); reading++; mutex_unlock(&m);
/* Reading here */
mutex_lock(&m); reading--; cond_broadcast(&turn); mutex_unlock(&m);}
Easy solution: Wake everyone
void writer(void) { mutex_lock(&m); writers++; while (reading || writing) cond_wait(&turn, &m); writing++; mutex_unlock(&m);
/* Writing here */
mutex_lock(&m); writing--; writers--; cond_broadcast(&turn); mutex_unlock(&m);}
![Page 36: Condition Variables](https://reader035.vdocuments.net/reader035/viewer/2022062305/568164aa550346895dd6a729/html5/thumbnails/36.jpg)
36
Pitfalls
signal() before wait()• Logical error: Waiting thread will miss the signal
Fail to lock mutex before calling wait()• Might return error, or simply not block
if (!condition) wait(); instead of while (!condition) wait();• condition may still be false when wait returns!• can lead to arbitrary errors (e.g., following NULL pointer,
memory corruption, ...)
Forget to unlock mutex• uh oh...
![Page 37: Condition Variables](https://reader035.vdocuments.net/reader035/viewer/2022062305/568164aa550346895dd6a729/html5/thumbnails/37.jpg)
37
Forgetting to unlock the mutex
mreaderthread
void reader(void) { mutex_lock(&m); if (writers) cond_wait(&turn, &m); while (writing) cond_wait(&turn, &m); reading++; mutex_unlock(&m);
/* Reading here */
mutex_lock(&m); reading--; cond_broadcast(&turn); mutex_unlock(&m);}
while (1) { reader() };
Waiting for
held by
After running once,next time reader callsmutex_lock(&m):
![Page 38: Condition Variables](https://reader035.vdocuments.net/reader035/viewer/2022062305/568164aa550346895dd6a729/html5/thumbnails/38.jpg)
38
Forgetting to unlock the mutex
mreaderthread
Waiting for
held by
After running once,next time reader callsmutex_lock(&m):
DEADLOCKthread waits foreverfor event that willnever happen
![Page 39: Condition Variables](https://reader035.vdocuments.net/reader035/viewer/2022062305/568164aa550346895dd6a729/html5/thumbnails/39.jpg)
39
Semaphores vs. Condition VariablesSemaphore
• Integer value (≥ 0)• Wait doesn’t always block• Signal either un-blocks
thread or increments counter
• If signal releases thread, both may continue concurrently
Condition Variable• No value• Wait always blocks• Signal either un-blocks
thread or is lost• If signal releases thread,
only one continues Need to hold mutex
lock to proceed Other thread is
released from waiting on condition, but still has to wait to obtain the mutex again
![Page 40: Condition Variables](https://reader035.vdocuments.net/reader035/viewer/2022062305/568164aa550346895dd6a729/html5/thumbnails/40.jpg)
40
Conclusion
Condition variables• convenient way of signaling general-purpose events
between threads
Common implementation: “monitors”• An object which does the locking/unlocking for you when
its methods are called• See synchronized keyword in Java
Beware pitfalls...• especially deadlock: our next topic