University of Pennsylvania 9/21/00CSE 3801 Concurrent Process Synchronization (Lock and semaphore) CSE 380 Lecture Note 5 Insup Lee
University of Pennsylvania 9/21/00CSE 3802 Locks lock (x) performs lock: [ if x = false then x := true else go to lock ] unlock (x) performs [x := false ] E.g., var x : boolean parbegin P1:... lock(x); CS_1; unlock(x) … … Pn:... lock(x); CS_n; unlock(x) … parend
University of Pennsylvania 9/21/00CSE 3803 Properties 1. Starvation is possible. 2. Busy waiting. 3. Different locks may be used for different shared resources. 4. Proper use not enforced. E.g., forget to lock.
University of Pennsylvania 9/21/00CSE 3804 How to implement locks Requires an atomic (uninterruptable at the memory level) operations like test-and-set or swap. atomic function TestAndSet (var x: boolean): boolean; begin TestAndSet := x; x := true; end procedure Lock (var x : boolean); while TestAndSet(x) do skip od; procedure Unlock (var x: boolean); x := false; (1) If not supported by hardware, TestAndSet can be implemented by disabling and unabling interrupts. (2) Lock can also be implemented using atomic swap(x,y).
University of Pennsylvania 9/21/00CSE 3805 Hardware Instructions Examples: (1) VAX 11, (2) B6500 MIPS -- Load-Linked/Store Conditional (LL/SC) Pentium -- Compare and Exchange, Exchange, Fetch and Add SPARC -- Load Store Unsigned Bit (LDSTUB) in v9 PowerPC -- Load Word and Reserve (lwarx) and Store Word Conitional (stwcx)
University of Pennsylvania 9/21/00CSE 3806 (1) VAX 11 BBSSI Branch on Bit Set and Set Interlocked. BBCCI Branch on Bit Clear and Clear Interlocked. op bitpos, var, displacement Operation: teststate = if {BBSSI} then 1 else 0 {set interlock} tmp := bit bit := teststate {release interlock} if tmp = teststate then pc := pc + displacement fi Busy waiting -- set bit and if already set then go to 1$ 1$: BBSSI bit, base, 1$
University of Pennsylvania 9/21/00CSE 3807 (2) B6500 Read with lock operation. SPIN: If RDLK (x) then go to SPIN; RDLK(x) register memory A: addr of x B: 1 x: Swap the contents of B and x in one memory cycle and return B.
University of Pennsylvania 9/21/00CSE 3808 Semaphores P V Dijkstra ‘65 wait signal Per Brinch Hansen The semaphore has a value that is invisible to the users and a queue of processes waiting to acquire the semaphore. type semaphore = record value : integer; L : list of process; end P(S):[ S.value := S.value-1; if S.value < 0 then add this process to S.L; block; end if ] V(S):[ S.value := S.value + 1; if S.value <= 0 then remove a process P from S.L; wakeup(P); end if ]
University of Pennsylvania 9/21/00CSE 3809 Properties of semaphore parbegin S.value = 1 P1:... P(S); CS1; V(S);... P2:... P(S); CS2; V(S); Pn:... P(S); CSn; V(S);... parend Properties No busy waiting May starve unless FCFS (scheduling left to the implementer of semaphores) Can handle multiple users by proper initialization. Example: 3 tape drivers Can implement scheduling on an precedence graph.
University of Pennsylvania 9/21/00CSE More properties and examples e.g. P2 P1 P4 P3 P6 P5 P1: Do Work P2: P(S12) P3: P(13) V(S12) Do Work Do Work V(S13) V(S24) V(S34) V(S35) 5Proper use can't be enforced by compiler. e.g. P(S) V(S) CS CS V(S) P(S) e.g. S1, S2 P1: P(S1) P2: P(S2) P(S2) P(S1) CS CS V(S2) V(S1) V(S1) V(S2)
University of Pennsylvania 9/21/00CSE Classical problems The bounded buffer problem The readers and writers problems The sleeping barber problem The dining philosophers problem
University of Pennsylvania 9/21/00CSE The Producer-Consumer Problem bounded buffer (of size n) one set of processes (producers) write to it one set of processes (consumers) read from it semaphore: full = 0 /* counting semaphores */ empty = n mutex = 1 /* binary semaphore */ process Producer process Consumer do forever do forever. P(full) /* produce */ P(mutex). /* take from buffer */ P(empty) V(mutex) P(mutex) V(empty) /* add to buffer */. V(mutex) /* consume */ V(full). end end
University of Pennsylvania 9/21/00CSE The Dining Philosopher Problem Five philosopher spend their lives thinking + eating. One simple solution is to represent each chopstick by a semaphore. P before picking it up & V after using it. var chopstick: array[0..4] of semaphores=1 philosopher i repeat P( chopstock[i] ); P( chopstock[i+1 mod 5] );... eat... V( chopstock[i] ); V( chopstock[i+1 mod 5] );... think... forever Is deadlock possible?
University of Pennsylvania 9/21/00CSE Number of possible states o5 philosophers oLocal state (LC) for each philosoper thinking, waiting, eating oGlabal state = (LC 1, LC 2, …, LC5) E.g., (thinking, waiting, waiting, eating, thinking) E.g., (waiting, eating, waiting, eating, waiting) oSo, the number of global states are 3 ** 5 = 243 oActually, it is a lot more than this since waiting can be Waiting for the first fork Waiting for the second fork
University of Pennsylvania 9/21/00CSE Number of possible behaviors Sequence of states Initial state: (thinking,thinking,thinking,thinking,thinking) The number of possible behaviors = 5 x 5 x 5 x … Deadlock state: (waiting,waiting,waiting,waiting, waiting) Given the state transition model of your implementation, show that it is not possible to reach the deadlock state from the initial state.
University of Pennsylvania 9/21/00CSE The Readers and Writers Problem Shared data to be accessed in two modes: reading and writing. –Any number of processes permitted to read at one time –writes must exclude all other operations. Read Write Read Y N conflict Write N N matrix Intuitively: Reader: | Writer: when(no_writers==0) do | when(no_readers==0 no_readers=no_readers+1 | and no_writers==0) do | no_writers = 1 | | | no_readers=no_readers-1 | no_writers = 0. |.. |.
University of Pennsylvania 9/21/00CSE A Solution to the R/W problem Semaphore: mutex = 1 /* mutual excl. for updating readcount */ wrt = 1 /* mutual excl. writer */ int variable: readcount = 0 Reader: P(mutex) readcount = readcount + 1 if readcount == 1 then P(wrt) V(mutex) P(mutex) readcount = readcount – 1 if readcount == 0 then V(wrt) V(mutex) Writer: P(wrt) V(wrt) Notes: wrt also used by first/last reader that enters/exits critical section. Solution gives priority to readers in that writers can be starved by stream of readers.
University of Pennsylvania 9/21/00CSE nd assignment Use semaphores Need to have shared memory between processes to allocate semaphores
University of Pennsylvania 9/21/00CSE Semaphores A semaphore is a non-negative integer count and is generally used to coordinate access to resources System calls: int sema_init(sema_t *sp, unsigned int count, int type, void * arg): Initialize semaphores pointed to by sp to count. type can assign several different types of behavior to a semaphore int sema_destroy(sema_t *sp); destroys any state related to the semaphore pointed to by sp. The semaphore storage space is not released. int sema_wait(sema_t *sp); blocks the calling thread until the semaphore count pointed to by sp is greater than zero, and then it atomically decrements the count.
University of Pennsylvania 9/21/00CSE Semaphores (cont’d) int sema_trywait(sema_t *sp); atomically decrements the semaphore count pointed to by sp, if the count is greater than zero; otherwise, it returns an error. int sema_post(sema_t *sp); atomically increments the semaphore count pointed to by sp. If there are any threads blocked on the semaphore,one will be unblocked.
University of Pennsylvania 9/21/00CSE Example The customer waiting-line in a bank is analogous to the synchronization scheme of a semaphore using sema_wait() and sema_trywait():
University of Pennsylvania 9/21/00CSE Semaphores example #include #define TELLERS 10 sema_t tellers; /* semaphore */ int banking_hours(), deposit_withdrawal; void *customer(), do_business(), skip_banking_today();... sema_init(&tellers, TELLERS, USYNC_THREAD, NULL); /* 10 tellers available */ while(banking_hours()) pthread_create(NULL, NULL, customer, deposit_withdrawal);... void * customer(int deposit_withdrawal) { int this_customer, in_a_hurry = 50; this_customer = rand() % 100;
University of Pennsylvania 9/21/00CSE if (this_customer == in_a_hurry) { if (sema_trywait(&tellers) != 0) if (errno == EAGAIN) { /* no teller available */ skip_banking_today(this_customer); return; } /* else go immediately to available teller and decrement tellers */ } else sema_wait(&tellers); /* wait for next teller, then proceed, and decrement tellers */ do_business(deposit_withdrawal); sema_post(&tellers); /* increment tellers; this_customer's teller is now available */ }