Presentation is loading. Please wait.

Presentation is loading. Please wait.

Condition Variables Revisited Copyright ©: University of Illinois CS 241 Staff1.

Similar presentations


Presentation on theme: "Condition Variables Revisited Copyright ©: University of Illinois CS 241 Staff1."— Presentation transcript:

1 Condition Variables Revisited Copyright ©: University of Illinois CS 241 Staff1

2 Condition Variable Without condition variables,  Threads continually poll to check if the condition is met  Busy waiting! With condition variables  Same goal without polling Copyright ©: University of Illinois CS 241 Staff2

3 Inside a condition variable struct pthread_cond { int waiting; handle_t semaphore; }; Copyright ©: University of Illinois CS 241 Staff3 Number of threads waiting on the condition variable A semaphore for synchronization

4 Inside a condition variable int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) { atomic_increment(&cond->waiting); pthread_mutex_unlock(mutex); if (wait(cond->semaphore, INFINITE) < 0) return errno; atomic_decrement(&cond->waiting); pthread_mutex_lock(mutex); return 0; } Copyright ©: University of Illinois CS 241 Staff4 pthread_mutex_lock is always called before pthread_cond_wait to acquire lock Have lock int pthread_cond_signal(pthread_cond_t *cond) { if (cond->waiting) semrel(cond->semaphore, 1); return 0; } Have lock Thread always has lock when returning from pthread_cond_wait

5 More Complex Example Master thread  Spawns a number of concurrent slaves  Waits until all of the slaves have finished to exit  Tracks current number of slaves executing A mutex is associated with count and a condition variable with the mutex Copyright ©: University of Illinois CS 241 Staff5

6 Example #include #define NO_OF_PROCS 4 typedef struct _SharedType { int count; /* number of active slaves */ pthread_mutex_t lock; /* mutex for count */ pthread_cond_t done; /* sig. by finished slave */ } SharedType, *SharedType_ptr; SharedType_ptr shared_data; Copyright ©: University of Illinois CS 241 Staff6

7 Example: Main main(int argc, char **argv) { int res; /* allocate shared data */ if ((sh_data = (SharedType *) malloc(sizeof(SharedType))) == NULL) { exit(1); } sh_data->count = 0; /* allocate mutex */ if ((res = pthread_mutex_init(&sh_data- >lock, NULL)) != 0) { exit(1); } /* allocate condition var */ if ((res = pthread_cond_init(&sh_data- >done, NULL)) != 0) { exit(1); } /* generate number of slaves to create */ srandom(0); /* create up to 15 slaves */ master((int) random()%16); } Copyright ©: University of Illinois CS 241 Staff7

8 Example: Main main(int argc, char **argv) { int res; /* allocate shared data */ if ((sh_data = (SharedType *) malloc(sizeof(SharedType))) == NULL) { exit(1); } sh_data->count = 0; /* allocate mutex */ if ((res = pthread_mutex_init(&sh_data- >lock, NULL)) != 0) { exit(1); } /* allocate condition var */ if ((res = pthread_cond_init(&sh_data- >done, NULL)) != 0) { exit(1); } /* generate number of slaves to create */ srandom(0); /* create up to 15 slaves */ master((int) random()%16); } Copyright ©: University of Illinois CS 241 Staff8 pthread_mutex_t data_mutex = PTHREAD_MUTEX_INITIALIZER; pthread_cont_t data_cond = PTHREAD_COND_INITIALIZER;

9 Example: Master master(int nslaves) { int i; pthread_t id; for (i = 1; i <= nslaves; i += 1) { pthread_mutex_lock(&sh_data- >lock); /* start slave and detach */ shared_data->count += 1; pthread_create(&id, NULL, (void* (*)(void *))slave, (void *)sh_data); pthread_mutex_unlock(&sh_data- >lock); } pthread_mutex_lock(&sh_data- >lock); while (sh_data->count != 0) pthread_cond_wait(&sh_data- >done, &sh_data->lock); pthread_mutex_unlock(&sh_data- >lock); printf("All %d slaves have finished.\n", nslaves); pthread_exit(0); } Copyright ©: University of Illinois CS 241 Staff9

10 Example: Slave void slave(void *shared) { int i, n; sh_data = shared; printf(“Slave.\n", n); n = random() % 1000; for (i = 0; i < n; i+= 1) Sleep(10); /* mutex for shared data */ pthread_mutex_lock(&sh_data- >lock); /* dec number of slaves */ sh_data->count -= 1; /* done running */ printf("Slave finished %d cycles.\n", n); /* signal that you are done working */ pthread_cond_signal(&sh_data- >done); /* release mutex for shared data */ pthread_mutex_unlock(&sh_data- >lock); } Copyright ©: University of Illinois CS 241 Staff10

11 Semaphores vs. CVs Semaphore Integer value (>=0) Wait does not always block Signal either releases thread or inc’s counter If signal releases thread, both threads continue afterwards Condition Variables No integer value Wait always blocks Signal either releases thread or is lost If signal releases thread, only one of them continue Copyright ©: University of Illinois CS 241 Staff11

12 Classical Synchronization Problems 12Copyright ©: University of Illinois CS 241 Staff

13 This lecture Goals  Introduce classical synchronization problems Topics  Producer-Consumer Problem  Reader-Writer Problem  Dining Philosophers Problem  Sleeping Barber’s Problem 13Copyright ©: University of Illinois CS 241 Staff

14 Chefs cook items and put them on a conveyer belt 14Copyright ©: University of Illinois CS 241 Staff Waiters pick items off the belt

15 Now imagine many chefs! 15Copyright ©: University of Illinois CS 241 Staff And many waiters!

16 A potential mess! 16Copyright ©: University of Illinois CS 241 Staff

17 Producer-Consumer Problem Producers insert items Consumers remove items Shared resource: bounded buffer  Efficient implementation: circular buffer with an insert and a removal pointer Chef = Producer Waiter= Consumer 17Copyright ©: University of Illinois CS 241 Staff

18 Producer-Consumer 18Copyright ©: University of Illinois CS 241 Staff Chef = Producer Waiter= Consumer

19 Producer-Consumer insertPtr removePtr 19Copyright ©: University of Illinois CS 241 Staff Chef = Producer Waiter= Consumer What does the chef do with a new pizza? Where does the waiter take a pizza from?

20 Producer-Consumer insertPtr removePtr Chef = Producer Waiter= Consumer 20Copyright ©: University of Illinois CS 241 Staff Insert pizza insertPtr

21 Producer-Consumer insertPtr removePtr Chef = Producer Waiter= Consumer 21Copyright ©: University of Illinois CS 241 Staff Insert pizza

22 Producer-Consumer insertPtr removePtr Chef = Producer Waiter= Consumer 22Copyright ©: University of Illinois CS 241 Staff Insert pizza

23 Producer-Consumer insertPtr removePtr Chef = Producer Waiter= Consumer 23Copyright ©: University of Illinois CS 241 Staff Remove pizza removePtr

24 Producer-Consumer insertPtr removePtr Chef = Producer Waiter= Consumer 24Copyright ©: University of Illinois CS 241 Staff Insert pizza

25 Producer-Consumer insertPtr removePtr Chef = Producer Waiter= Consumer 25Copyright ©: University of Illinois CS 241 Staff Insert pizza

26 Producer-Consumer insertPtr removePtr BUFFER FULL: Producer must be blocked! Chef = Producer Waiter= Consumer 26Copyright ©: University of Illinois CS 241 Staff Insert pizza

27 Producer-Consumer insertPtr removePtr Chef = Producer Waiter= Consumer 27Copyright ©: University of Illinois CS 241 Staff Remove pizza

28 Producer-Consumer insertPtr removePtr Chef = Producer Waiter= Consumer 28Copyright ©: University of Illinois CS 241 Staff Remove pizza

29 Producer-Consumer insertPtr removePtr Chef = Producer Waiter= Consumer 29Copyright ©: University of Illinois CS 241 Staff Remove pizza

30 Producer-Consumer insertPtr removePtr Chef = Producer Waiter= Consumer 30Copyright ©: University of Illinois CS 241 Staff Remove pizza

31 Producer-Consumer insertPtr removePtr Chef = Producer Waiter= Consumer 31Copyright ©: University of Illinois CS 241 Staff Remove pizza

32 Producer-Consumer insertPtr removePtr Chef = Producer Waiter= Consumer 32Copyright ©: University of Illinois CS 241 Staff Remove pizza

33 Producer-Consumer insertPtr removePtr Chef = Producer Waiter= Consumer 33Copyright ©: University of Illinois CS 241 Staff Remove pizza

34 Producer-Consumer insertPtr removePtr BUFFER EMPTY: Consumer must be blocked! Chef = Producer Waiter= Consumer 34Copyright ©: University of Illinois CS 241 Staff Remove pizza

35 Producer-Consumer Summary Producer  Insert items  Update insertion pointer Consumer  Execute destructive read on the buffer  Update removal pointer Both  Update information about how full/empty the buffer is Solution  Must allow multiple producers and consumers Copyright ©: University of Illinois CS 241 Staff35

36 Challenges Prevent buffer overflow Prevent buffer underflow Mutual exclusion when modifying the buffer data structure 36Copyright ©: University of Illinois CS 241 Staff

37 Solutions Prevent buffer overflow  Block producer when full  Counting semaphore to count #free slots  0  block producer Prevent buffer underflow Mutual exclusion when modifying the buffer data structure 37Copyright ©: University of Illinois CS 241 Staff

38 Solutions Prevent buffer overflow  Block producer when full  Counting semaphore to count #free slots  0  block producer Prevent buffer underflow  Block consumer when empty  Counting semaphore to count #items in buffer  0  block consumer Mutual exclusion when modifying the buffer data structure 38Copyright ©: University of Illinois CS 241 Staff

39 Solutions Prevent buffer overflow  Block producer when full  Counting semaphore to count #free slots  0  block producer Prevent buffer underflow  Block consumer when empty  Counting semaphore to count #items in buffer  0  block consumer Mutual exclusion when modifying the buffer data structure  Mutex protects shared buffer & pointers 39Copyright ©: University of Illinois CS 241 Staff

40 Assembling the solution Producer  sem_wait(slots), sem_signal(slots)  Initialize slots to N Consumer  sem_wait(items), sem_signal(items)  Initialize semaphore items to 0 Synchronization  mutex_lock(m), mutex_unlock(m) Buffer management  insertptr = (insertptr+1) % N  removalptr = (removalptr+1) % N Copyright ©: University of Illinois CS 241 Staff40

41 Producer-Consumer Code buffer[ insertPtr ] = data; insertPtr = (insertPtr + 1) % N; result = buffer[removePtr]; removePtr = (removePtr +1) % N; Copyright ©: University of Illinois CS 241 Staff41 Critical Section: move insert pointer Critical Section: move remove pointer

42 Producer-Consumer Code sem_wait(slots); mutex_lock(mutex); buffer[ insertPtr ] = data; insertPtr = (insertPtr + 1) % N; mutex_unlock(mutex); sem_signal(items); sem_wait(items); mutex_lock(mutex); result = buffer[removePtr]; removePtr = (removePtr +1) % N; mutex_unlock(mutex); sem_signal(slots); Copyright ©: University of Illinois CS 241 Staff42 Block if there are no free slots Block if there are no items to take Counting semaphore – check and decrement the number of free slots Counting semaphore – check and decrement the number of available items Done – increment the number of available items Done – increment the number of free slots

43 Consumer Pseudocode: getItem() sem_wait(items); mutex_lock(mutex); result = buffer[removePtr]; removePtr = (removePtr +1) % N; mutex_unlock(mutex); sem_signal(slots); Copyright ©: University of Illinois CS 241 Staff43 Error checking/EINTR handling not shown

44 Producer Pseudocode: putItem(data) sem_wait(slots); mutex_lock(mutex); buffer[ insertPtr ] = data; insertPtr = (insertPtr + 1) % N; mutex_unlock(mutex); sem_signal(items); Copyright ©: University of Illinois CS 241 Staff44 Error checking/EINTR handling not shown

45 Shared Resource Readers-Writers Problem

46 Shared Resource Readers-Writers Problem

47 Shared Resource Readers-Writers Problem

48 II. Reader-Writer Problem Readers read data Writers write data Rules  Multiple readers may read the data simultaneously  Only one writer can write the data at any time  A reader and a writer cannot access data simultaneously Locking table  Whether any two can be in the critical section simultaneously Copyright ©: University of Illinois CS 241 Staff48 ReaderWriter Reader OKNo WriterNo

49 reader() { while(TRUE) { ; sem_wait(mutex); readCount++; if(readCount == 1) sem_wait(writeBlock); sem_signal(mutex); /* Critical section */ access(resource); sem_wait(mutex); readCount--; if(readCount == 0) sem_signal(writeBlock); sem_post(mutex); } writer() { while(TRUE) { ; sem_wait(writeBlock); /* Critical section */ access(resource); sem_signal(writeBlock); } Reader-Writer: First Solution int readCount = 0; semaphore mutex = 1; semaphore writeBlock = 1;

50 reader() { while(TRUE) { ; sem_wait(mutex); readCount++; if(readCount == 1) sem_wait(writeBlock); sem_signal(mutex); /* Critical section */ access(resource); sem_wait(mutex); readCount--; if(readCount == 0) sem_signal(writeBlock); sem_post(mutex); } writer() { while(TRUE) { ; sem_wait(writeBlock); /* Critical section */ access(resource); sem_signal(writeBlock); } First reader competes with writers Last reader signals writers Reader-Writer: First Solution int readCount = 0; semaphore mutex = 1; semaphore writeBlock = 1; First reader blocks writers Last reader unblocks writers Writer blocks all readers

51 reader() { while(TRUE) { ; sem_wait(mutex); readCount++; if(readCount == 1) sem_wait(writeBlock); sem_signal(mutex); /* Critical section */ access(resource); sem_wait(mutex); readCount--; if(readCount == 0) sem_signal(writeBlock); sem_post(mutex); } writer() { while(TRUE) { ; sem_wait(writeBlock); /* Critical section */ access(resource); sem_signal(writeBlock); } Any writer must wait for all readers → Readers can starve writers → Updates can be delayed forever → Not desirable! Reader-Writer: First Solution int readCount = 0; semaphore mutex = 1; semaphore writeBlock = 1; First reader blocks writers Last reader unblocks writers

52 reader() { while(TRUE) { ; sem_wait(readBlock); sem_wait(mutex1); readCount++; if(readCount == 1) sem_wait(writeBlock); sem_signal(mutex1); sem_signal(readBlock); access(resource); sem_wait(mutex1); readCount--; if(readCount == 0) sem_signal(writeBlock); sem_signal(mutex1); } writer() { while(TRUE) { ; sem_wait(mutex2); writeCount++; if(writeCount == 1) sem_wait(readBlock); sem_signal(mutex2); sem_wait(writeBlock); access(resource); sem_signal(writeBlock); sem_wait(mutex2); writeCount--; if(writeCount == 0) sem_signal(readBlock); sem_signal(mutex2); } Reader-Writer: Second Solution int readCount=0, writeCount=0; semaphore mutex1=1, mutex2=1; Semaphore readBlock=1,writeBlock=1

53 reader() { while(TRUE) { ; sem_wait(readBlock); sem_wait(mutex1); readCount++; if(readCount == 1) sem_wait(writeBlock); sem_signal(mutex1); sem_signal(readBlock); access(resource); sem_wait(mutex1); readCount--; if(readCount == 0) sem_signal(writeBlock); sem_signal(mutex1); } writer() { while(TRUE) { ; sem_wait(mutex2); writeCount++; if(writeCount == 1) sem_wait(readBlock); sem_signal(mutex2); sem_wait(writeBlock); access(resource); sem_signal(writeBlock); sem_wait(mutex2); writeCount--; if(writeCount == 0) sem_signal(readBlock); sem_signal(mutex2); } Reader-Writer: Second Solution int readCount=0, writeCount=0; semaphore mutex1=1, mutex2=1; Semaphore readBlock=1,writeBlock=1 Wait for writers First writer blocks readers Last writer unblocks readers

54 reader() { while(TRUE) { ; sem_wait(readBlock); sem_wait(mutex1); readCount++; if(readCount == 1) sem_wait(writeBlock); sem_signal(mutex1); sem_signal(readBlock); access(resource); sem_wait(mutex1); readCount--; if(readCount == 0) sem_signal(writeBlock); sem_signal(mutex1); } writer() { while(TRUE) { ; sem_wait(mutex2); writeCount++; if(writeCount == 1) sem_wait(readBlock); sem_signal(mutex2); sem_wait(writeBlock); access(resource); sem_signal(writeBlock); sem_wait(mutex2); writeCount--; if(writeCount == 0) sem_signal(readBlock); sem_signal(mutex2); } Reader-Writer: Second Solution int readCount=0, writeCount=0; semaphore mutex1=1, mutex2=1; Semaphore readBlock=1,writeBlock=1 Writers can starve readers → Reads can be delayed forever → Not desirable, either!

55 Better R-W solution idea Idea: serve requests in order  Once a writer requests access, any entering readers have to block until the writer is done Advantage? Disadvantage? 55Copyright ©: University of Illinois CS 241 Staff

56 reader() { while(TRUE) { ; sem_wait(writePending); sem_wait(readBlock); sem_wait(mutex1); readCount++; if(readCount == 1) sem_wait(writeBlock); sem_signal(mutex1); sem_signal(readBlock); sem_signal(writePending); access(resource); sem_wait(mutex1); readCount--; if(readCount == 0) sem_signal(writeBlock); sem_signal(mutex1); } writer() { while(TRUE) { ; sem_wait(writePending); sem_wait(mutex2); writeCount++; if(writeCount == 1) sem_wait(readBlock); sem_signal(mutex2); sem_wait(writeBlock); access(resource); sem_signal(writeBlock); sem_signal(writePending); sem_wait(mutex2); writeCount--; if(writeCount == 0) sem_signal(readBlock); sem_signal(mutex2); } Reader-Writer: Fairer Solution? int readCount = 0, writeCount = 0; semaphore mutex1 = 1, mutex2 = 1; semaphore readBlock = 1, writeBlock = 1, writePending = 1;

57 reader() { while(TRUE) { ; sem_wait(writePending); sem_wait(readBlock); sem_wait(mutex1); readCount++; if(readCount == 1) sem_wait(writeBlock); sem_signal(mutex1); sem_signal(readBlock); sem_signal(writePending); access(resource); sem_wait(mutex1); readCount--; if(readCount == 0) sem_signal(writeBlock); sem_signal(mutex1); } writer() { while(TRUE) { ; sem_wait(writePending); sem_wait(mutex2); writeCount++; if(writeCount == 1) sem_wait(readBlock); sem_signal(mutex2); sem_wait(writeBlock); access(resource); sem_signal(writeBlock); sem_signal(writePending); sem_wait(mutex2); writeCount--; if(writeCount == 0) sem_signal(readBlock); sem_signal(mutex2); } Reader-Writer: Fairer Solution? int readCount = 0, writeCount = 0; semaphore mutex1 = 1, mutex2 = 1; semaphore readBlock = 1, writeBlock = 1, writePending = 1; Wait for a pending writers that arrived before me Writer blocks subsequent readers

58 4131 reader() { while(TRUE) { ; sem_wait(writePending); sem_wait(readBlock); sem_wait(mutex1); readCount++; if(readCount == 1) sem_wait(writeBlock); sem_signal(mutex1); sem_signal(readBlock); sem_signal(writePending); access(resource); sem_wait(mutex1); readCount--; if(readCount == 0) sem_signal(writeBlock); sem_signal(mutex1); } writer() { while(TRUE) { ; sem_wait(writePending); sem_wait(mutex2); writeCount++; if(writeCount == 1) sem_wait(readBlock); sem_signal(mutex2); sem_wait(writeBlock); access(resource); sem_signal(writeBlock); sem_signal(writePending); sem_wait(mutex2); writeCount--; if(writeCount == 0) sem_signal(readBlock); sem_signal(mutex2); } Reader-Writer: Fairer Solution? int readCount = 0, writeCount = 0; semaphore mutex1 = 1, mutex2 = 1; semaphore readBlock = 1, writeBlock = 1, writePending = 1;

59 431 reader() { while(TRUE) { ; sem_wait(writePending); sem_wait(readBlock); sem_wait(mutex1); readCount++; if(readCount == 1) sem_wait(writeBlock); sem_signal(mutex1); sem_signal(readBlock); sem_signal(writePending); access(resource); sem_wait(mutex1); readCount--; if(readCount == 0) sem_signal(writeBlock); sem_signal(mutex1); } writer() { while(TRUE) { ; sem_wait(writePending); sem_wait(mutex2); writeCount++; if(writeCount == 1) sem_wait(readBlock); sem_signal(mutex2); sem_wait(writeBlock); access(resource); sem_signal(writeBlock); sem_signal(writePending); sem_wait(mutex2); writeCount--; if(writeCount == 0) sem_signal(readBlock); sem_signal(mutex2); } Reader-Writer: Fairer Solution? int readCount = 0, writeCount = 0; semaphore mutex1 = 1, mutex2 = 1; semaphore readBlock = 1, writeBlock = 1, writePending = 1; 1

60 reader() { while(TRUE) { ; sem_wait(writePending); sem_wait(readBlock); sem_wait(mutex1); readCount++; if(readCount == 1) sem_wait(writeBlock); sem_signal(mutex1); sem_signal(readBlock); sem_signal(writePending); access(resource); sem_wait(mutex1); readCount--; if(readCount == 0) sem_signal(writeBlock); sem_signal(mutex1); } writer() { while(TRUE) { ; sem_wait(writePending); sem_wait(mutex2); writeCount++; if(writeCount == 1) sem_wait(readBlock); sem_signal(mutex2); sem_wait(writeBlock); access(resource); sem_signal(writeBlock); sem_signal(writePending); sem_wait(mutex2); writeCount--; if(writeCount == 0) sem_signal(readBlock); sem_signal(mutex2); } Reader-Writer: Fairer Solution? int readCount = 0, writeCount = 0; semaphore mutex1 = 1, mutex2 = 1; semaphore readBlock = 1, writeBlock = 1, writePending = 1; When a reader arrives and a writer is waiting, the reader is suspended behind the writer → Less concurrency → Lower performance

61 Summary Classic synchronization problems  Producer-Consumer Problem  Reader-Writer Problem Saved for next time:  Sleeping Barber’s Problem  Dining Philosophers Problem 61Copyright ©: University of Illinois CS 241 Staff

62 Dining Philosophers N philosophers and N forks  Philosophers eat/think  Eating needs 2 forks  Pick one fork at a time 2 62Copyright ©: University of Illinois CS 241 Staff


Download ppt "Condition Variables Revisited Copyright ©: University of Illinois CS 241 Staff1."

Similar presentations


Ads by Google