by: | Burt Rosenberg |
at: | university of miami |
last-update: | 14 oct 2024 |
On S.P: decrement S.counter, and if S.counter is negative, place the calling thread on the wait queue, S.queue, and sleep the thread.
On S.V: increment S.counter. And if S.queue is not empty, remove any thread from S.quque and resume the thread.
These operations of fully atomic and synchronized. That is, simultaneous calls to S operations will be carried out such that each call runs in a time ordering from start to finish.
Here is why this is important. Without synchronization, starting with S.counter = 1, if both threads call S.P simultaneously, both threads see S.counter == 1, both threads set S.counter to 0, and both threads continue.
This is incorrect behavior. If two threads call S.P simultaneously, one P operation must occur fully before the other P operation. And with this contraint, exactly one of the two threads sees S.counter == 1, sets it to 0 and continues. The other thread sees S.counter == 0, sets it to -1, and sleeps. That is the correct behavior.
struct PV_Semaphore { int lock ; int count ; struct WaitList * waitlist ; } extern thread_t current_thread ; void P(struct PV_Semaphore * s) { while (test_and_set(\amp;s->lock,1)==1) ; s->count -- ; if (s->count<0) { s->waitlist.add(current_thread) ; set_state(current_thread, WAITING) ; s->lock = 0 ; current_thread.yield() ; return ; } s->lock = 0 ; return ; } void V(struct PV_Semaphore * s) { while (test_and_set(\amp;s->lock,1)==1) ; s->count ++ ; if (!s->waitlist.is_empty()) { t = s->waitlist.remmove() ; // remove one at random set_state(t, READY) ; } s->lock = 0 ; return ; }
=== locked ~~~ wait/sleep P V | | A: ----+=============+--------------- | | B: --------+~~~~~~~~~+======+-------- | | P V
There is one such lock for each object instance. Re-entrantcy (re-acquiring a lock already held) and leave are handled correctly as part of the language.
// solution to monitor project // burt rosenberg, oct 2010 // updated: oct 2011 // REMEMBER THIS IS PSEUDO-CODE! semaphore lock_sema semaphore wait_sema integer wait_count init: lock_sema unlocked (1) wait_sema locked (0) wait_count = 0 enter: assume: not holding lock_sema down on lock_sema leave: assume: holding lock_sema up on lock_sema wait: assume: holding lock_sema // increment must be atomic wait_count++ up on lock_sema // no critical race because wait_count // registers interest in waiting down on wait_sema // contend to reenter down on lock_sema notify: assume: holding lock_sema // are "if" and decrement SMP safe? yes, lock_sema protects wait_count. if wait_count>0 wait_count-- up on wait_sema notify_all: assume: holding lock_sema while wait_count>0 wait_count-- up on wait_sema
author: burton rosenberg
created: 14 oct 2024
update: 14 oct 2024