semaphores / reader/writer 1
Changelog Changes made in this version not seen in fjrst lecture: 1 October 2019: fjx mixup of ‘result’ and ‘value’ in semaphore exercise return 3 October 2019: correct reader-priority rwlock code to include readers == 0 check before signaling in ReadUnlock 1
last time monitors = mutex + condition variable mutex protects shared data important: locked mutex = whether thread should wait wont’ change condition variable (CV): abstracts queue of waiting threads CV wait: unlock a mutex + start waiting on queue done simultaneously so thread doesn’t miss its signal to wake up spurious wakeups — need to double-check condition CV broadcast: remove all threads from CV queue, have them reacquire lock CV signal: remove one threads from CV queue, have it reacquire lock no guarantee that it reacquire lock fjrst (except rare Hoare-style monitors) so thread needs to double-check condition even with no spurious wakeups 2
monitor exercise (1) pthread_mutex_unlock(&lock); } return item; pthread_mutex_unlock(&lock); item = buffer.dequeue(); } pthread_cond_wait(&data_ready, &lock); while (buffer.empty()) { pthread_mutex_lock(&lock); Consume() { } pthread_cond_signal(&data_ready); suppose we want producer/consumer, but… buffer.enqueue(item); pthread_mutex_lock(&lock); Produce(item) { UnboundedQueue buffer; pthread_cond_t data_ready; pthread_mutex_t lock; what should we change below? with each getting one item and don’t want two calls to ConsumeTwo() to wait… but change to ConsumeTwo() which returns a pair of values 3
monitor exercise: solution (1) ConsumeTwo() { } return Combine(item1, item2); pthread_mutex_unlock(&lock); item1 = buffer.dequeue(); item2 = buffer.dequeue(); while (buffer.size() < 2) { pthread_cond_wait(&data_ready, &lock); } pthread_mutex_lock(&lock); } (one of many possible solutions) pthread_mutex_unlock(&lock); if (buffer.size() > 1) { pthread_cond_signal(&data_ready); } buffer.enqueue(item); pthread_mutex_lock(&lock); Produce() { Assuming ConsumeTwo replaces Consume: 4
monitor exercise: solution 2 item = buffer.dequeue(); } return Combine(item1, item2); pthread_mutex_unlock(&lock); item1 = buffer.dequeue(); item2 = buffer.dequeue(); while (buffer.size() < 2) { pthread_cond_wait(&two_ready, &lock); } pthread_mutex_lock(&lock); ConsumeTwo() { } return item; pthread_mutex_unlock(&lock); while (buffer.size() < 1) { pthread_cond_wait(&one_ready, &lock); } (one of many possible solutions) pthread_mutex_lock(&lock); Consume() { } pthread_mutex_unlock(&lock); if (buffer.size() > 1) { pthread_cond_signal(&two_ready); } pthread_cond_signal(&one_ready); buffer.enqueue(item); pthread_mutex_lock(&lock); Produce() { Assuming ConsumeTwo is in addition to Consume (using two CVs): 5
monitor exercise: slow solution item = buffer.dequeue(); } return Combine(item1, item2); pthread_mutex_unlock(&lock); item1 = buffer.dequeue(); item2 = buffer.dequeue(); while (buffer.size() < 2) { pthread_cond_wait(&data_ready, &lock); } pthread_mutex_lock(&lock); ConsumeTwo() { } return item; pthread_mutex_unlock(&lock); while (buffer.size() < 1) { pthread_cond_wait(&data_ready, &lock); } (one of many possible solutions) pthread_mutex_lock(&lock); Consume() { } pthread_mutex_unlock(&lock); pthread_cond_broadcast(&data_ready); // broadcast and not signal, b/c we might wakeup only ConsumeTwo() otherwise buffer.enqueue(item); pthread_mutex_lock(&lock); Produce() { Assuming ConsumeTwo is in addition to Consume (using one CV): 6
monitor exercise (2) suppose we want to implement a one-use barrier } _____________________ _____________________ _____________________ ++number_reached; }; ___________________ int number_reached; // initially 0 // initially total # of threads int total_threads; pthread_mutex_t lock; struct BarrierInfo { what goes in the blanks? 7 void BarrierWait(BarrierInfo *barrier) { pthread_mutex_lock(&barrier − >lock); pthread_mutex_unlock(&barrier − >lock);
mutex/cond var init/destroy pthread_mutex_t mutex; pthread_cond_t cv; pthread_mutex_init(&mutex, NULL); pthread_cond_init(&cv, NULL); // --OR-- pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t cv = PTHREAD_COND_INITIALIZER; // and when done: ... pthread_cond_destroy(&cv); pthread_mutex_destroy(&mutex); 8
generalizing locks: semaphores semaphore has a non-negative integer value and two operations: P() or down or wait : then decerement by 1 V() or up or signal or post : increment semaphore by 1 (waking up thread if needed) P, V from Dutch: proberen (test), verhogen (increment) 9 wait for semaphore to become positive ( > 0 ),
semaphores are kinda integers semaphore like an integer, but… cannot read/write directly down/up operaion only way to access (typically) exception: initialization never negative — wait instead down operation wants to make negative? thread waits 10
reserving books suppose tracking copies of library book… Semaphore free_copies = Semaphore(3); void ReserveBook() { // wait for copy to be free free_copies.down(); } void ReturnBook() { free_copies.up(); // ... then wakekup waiting thread } 11 ... // ... then take reserved copy ... // return reserved copy
after calling down three times after calling down to reserve taken out taken out reserve book call down again start waiting… taken out counting resources: reserving books taken out reserve book call down waiting done waiting return book call up release waiter taken out to reserve all copies taken out free copies non-negative integer count = # how many books used? up = give back book; down = take book Copy 1 Copy 2 Copy 3 3 taken out suppose tracking copies of same library book 2 taken out after calling down to reserve taken out taken out taken out 12
after calling down three times taken out taken out taken out reserve book call down again start waiting… taken out counting resources: reserving books taken out reserve book call down waiting done waiting return book call up release waiter taken out to reserve all copies suppose tracking copies of same library book free copies non-negative integer count = # how many books used? up = give back book; down = take book Copy 1 Copy 2 Copy 3 3 taken out taken out 2 taken out after calling down to reserve taken out taken out 12 after calling down to reserve
after calling down three times after calling down to reserve taken out taken out reserve book call down again start waiting… taken out counting resources: reserving books taken out reserve book call down waiting done waiting return book call up release waiter taken out to reserve all copies taken out free copies non-negative integer count = # how many books used? up = give back book; down = take book Copy 1 Copy 2 Copy 3 2 taken out suppose tracking copies of same library book 2 taken out after calling down to reserve taken out taken out taken out 12
after calling down to reserve counting resources: reserving books taken out taken out reserve book call down again start waiting… taken out taken out taken out reserve book call down waiting done waiting return book call up release waiter taken out to reserve all copies suppose tracking copies of same library book free copies non-negative integer count = # how many books used? up = give back book; down = take book Copy 1 Copy 2 Copy 3 0 taken out 2 taken out after calling down to reserve taken out taken out taken out 12 after calling down three times
after calling down three times after calling down to reserve taken out taken out reserve book call down again start waiting… taken out counting resources: reserving books taken out reserve book call down waiting done waiting return book call up release waiter taken out to reserve all copies taken out free copies non-negative integer count = # how many books used? up = give back book; down = take book Copy 1 Copy 2 Copy 3 0 taken out suppose tracking copies of same library book 2 taken out after calling down to reserve taken out taken out taken out 12
after calling down three times after calling down to reserve taken out taken out reserve book call down again start waiting… taken out counting resources: reserving books taken out reserve book call down waiting done waiting return book call up release waiter taken out to reserve all copies taken out free copies non-negative integer count = # how many books used? up = give back book; down = take book Copy 1 Copy 2 Copy 3 0 taken out suppose tracking copies of same library book 2 taken out after calling down to reserve taken out taken out taken out 12
implementing mutexes with semaphores struct Mutex { } } } 13 Semaphore s; /* with inital value 1 */ /* value = 1 --> mutex if free */ /* value = 0 --> mutex is busy */ MutexLock(Mutex *m) { m − >s.down(); MutexUnlock(Mutex *m) { m − >s.up();
Recommend
More recommend