Process/Thread Synchronization (Part 2) Modified from Chapter 6 for 170 with Nachos threads, Tao Yang, 2012
What to learn? Software and hardware solutions of the synchronization primitives Deadlock/starvation More synchronization applications
Implementation of Synchronization Primitives Software solutions through shared memory data checking or message passing. Assume that the LOAD and STORE instructions are atomic for shared variables; that is, cannot be interrupted Hardware-assisted solutions Properties (e.g. for implementing a lock) Mutual exclusion: only one gets a lock. Progress: when multiple threads try to acquire a lock, one can get it if nobody has it. Bounded waiting: lock waiting time is limited.
Software Solution for 2 processes/threads Two processes use a shared variable to coordinate int turn=0; // whose turn for the critical section Process 0 Process 1: Mutual exclusion? Progress? Bounded waiting? while (turn != 0); critical section turn= 1; //give turn to P1 while (turn != 1); turn= 0; //give turn to P0
(turn==0) and (turn==1) is false Mutual exclusion? Prove by contraction. Assume both Processes 0 and 1 are in the critical section. Process 0 Process 1: (turn==0) and (turn==1) is false while (turn != 1); critical section turn= 0; //give turn to P0 while (turn != 0); turn= 1; //give turn to P1 turn==0 turn==1
Progress? while (turn != 0); turn= 1; The two processes use a shared variable to coordinate int turn=0; // whose turn for the critical section Process 0 Process 0 tries again: while (turn != 0); critical section turn= 1; Loop forever
Peterson’s Solution Two process software solution (Text book chapter 6.3) Assume that the LOAD and STORE instructions are atomic; that is, cannot be interrupted. The two processes share two variables: int turn; indicates whose turn it is to enter the critical section. Boolean ready[2] indicate if a process is ready to enter the critical section. ready[0] = true implies that process P0 is ready!
Peterson’s Algorithm Process P0: ready[0] = TRUE; turn = 1; while (ready[1] && turn == 1); Critical section ready[0] = FALSE; Ready to enter critical section; Wait if another process is in. Exit critical section Process P1: ready[1] = TRUE; turn = 0; while (ready[0] && turn == 0); Critical section ready[1] = FALSE; Mutual exclusion? Progress? Bounded waiting?
Mutual Execution? Prove by contradition Process P0: ready[0] = TRUE; turn = 1; while (ready[1] && turn == 1); critical section ready[0] = FALSE; ready[0]=T and (Ready[1]=F or turn=0) ready[1]=T and (Ready[0]=F or turn=1) Process P1: ready[1] = TRUE; turn = 0; while (ready[0] && turn == 0); critical section ready[1] = FALSE; Both conditions are true, which is not possible
Progress? Can both P0/P1 wait forever? Process P0: ready[0] = TRUE; turn = 1; while (ready[1] && turn == 1); critical section ready[0] = FALSE; Loop forever (Ready[1]=T and turn=1) (Ready[0]=T and turn=0) Process P1: ready[1] = TRUE; turn = 0; while (ready[0] && turn == 0); critical section ready[1] = FALSE; Loop forever Both conditions are true, which is not possible
Progress? Can P0 wait forever after P1 leaves Process P0: ready[0] = TRUE; turn = 1; while (ready[1] && turn == 1); critical section ready[0] = FALSE; Loop forever (ready[1]=T and turn=1) ready[1]=F Process P1: ready[1] = TRUE; turn = 0; while (ready[0] && turn == 0); critical section ready[1] = FALSE; Both conditions are true, which is not possible.
Hardware Solution for Synchronization Many systems provide hardware support for critical section code Uniprocessors – could disable interrupts Currently running code would execute without preemption Project 1. Generally too inefficient on multiprocessor systems Operating systems using this not broadly scalable Modern machines provide special atomic hardware instructions Atomic = non-interruptable Either test memory word and set value Or swap contents of two memory words
Atomic TestAndSet Instruction Definition: Fetch the value of a shared variable and then set it to TRUE atomically. boolean TestAndSet (boolean *target) { boolean rv = *target; *target = TRUE; return rv: }
Lock Solution using TestAndSet Shared boolean variable: lock. “True” means somebody has acquired the lock. initialized to FALSE. Solution: while ( TestAndSet (&lock )) ; // wait Critical section; lock = FALSE; Mutual exclusion? Progress? Bounded waiting? no
Mutual exclusion? Assume P0 gets first, and then P1 Property: Lock=T somebody is in the critical section Lock=F nobody is in the critical section. Process P0: while(TestAndSet (&lock)); critical section lock = FALSE; } Conditions C1 and then C2: cannot be true C1: lock was F in last TestAndSet(). TestAndSet() returns F Now lock is T. C2: lock was F in last TestAndSet(). Process P1: while(TestAndSet (&lock)); critical section lock = FALSE;
Bounded Waiting? Process P0: Process P1: TestAndSet (&lock)); //get in … Lock=FALSE; //get out TestAndSet(&lock);// get in Process P1: TestAndSet(&lock);// wait
Atomic Swap Instruction Definition: exchange values of two variables automatically. void Swap (boolean *a, boolean *b) { boolean temp = *a; *a = *b; *b = temp: }
Lock Solution using Swap Shared Boolean variable lock initialized to FALSE; Each process has a local Boolean variable key Solution: key = TRUE; while ( key == TRUE) Swap (&lock, &key ); Critical section lock = FALSE; Lock: False Key: True Mutual exclusion, progress. bounded waiting?
TestAndSet Solution with Bounded Waiting Enter Critical Section if: Lock =False or Waiting[i]==False Waiting[] T T F T Assign with cyclic order for fairness
Bounded-waiting Mutual Exclusion with TestAndSet() waiting[i] = TRUE; key = TRUE; while (waiting[i] && key) key = TestAndSet(&lock); waiting[i] = FALSE; Critical section j = (i + 1) % n; while ((j != i) && waiting[j]== False) j = (j + 1) % n; if (j == i) lock = FALSE; else waiting[j] = FALSE; Loop if this process should wait and Lock =True Find a waiting process j and set waiting[j]=False Or Set Lock=False
Deadlock and Starvation Deadlock – two or more processes (or threads) are waiting indefinitely for an event that can be only caused by one of these waiting processes Starvation – indefinite blocking. A process is in a waiting queue forever. Let S and Q be two locks: P0 P1 Acquire(S); Acquire(Q); Acquire (Q); Acquire (S); . . Release (Q); Release(S); Release (S); Release(Q);
Deadlock Avoidance Order the locks and always acquire the locks in that order. Eliminate circular waiting P0 P1 Acquire(S); Acquire(S); Acquire(Q); Acquire (Q); . . Release(Q); Release (Q); Release(S); Release (S);
Classical Problems of Synchronization Bounded-Buffer Problem Laundromat Barriers Readers and Writers Problem
Barriers
Barriers called multiple times
Implement a barrier int count=0; barrier(N) { //for N threads mylock->Acquire(); count ++; mylock->Release(); while (count <N) Cond->Wait(mylock); if(count==N) { Cond->Broadcast(mylock); count=0; } mylock->Release() What’s wrong with this? Count=N for next barrier() called in another thread
Readers-Writers Problem A data set is shared among a number of concurrent processes. Readers – only read the data set; they do not perform any updates Writers – can both read and write Requirement: allow multiple readers to read at the same time. Only one writer can access the shared data at the same time. Reader/writer access permission table: Reader Writer OK No NO
Readers-Writers (First try with 1 lock) do { wrt.Acquire(); // wrt is a lock // writing is performed wrt.Release(); } while (TRUE); Reader wrt.Acquire(); // Use wrt lock // reading is performed Reader Writer ?
2nd try using a lock + readcount writer do { wrt.Acquire(); // Use wrt lock // writing is performed wrt.Release(); } while (TRUE); Reader readcount++; // add a reader counter. if(readcount==1) wrt.Acquire(); // reading is performed readcount--; if(readcount==0) wrt.Release();
You may also use a binary semaphore writer do { wrt.P(); // Use wrt semaphore with initial value=1 // writing is performed wrt.V(); } while (TRUE); Reader readcount++; //initial value=0 if(readcount==1) wrt.P(); // reading is performed readcount--; if(readcount==0) wrt.V(); What’s wrong with this? readcount is not protected
Readers-Writers Problem (Text Book) Shared Data Data set Semaphore mutex initialized to 1 Semaphore wrt initialized to 1 Integer readcount initialized to 0
Readers-Writers Problem (textbook) The structure of a writer process do { wrt.P() ; //Lock wrt // writing is performed wrt.V() ; //Unlock wrt } while (TRUE);
Readers-Writers Problem (Cont.) The structure of a reader process do { mutex.P() ; readcount ++ ; if (readcount == 1) wrt.P() ; mutex.V() // reading is performed readcount - - ; if (readcount == 0) wrt.V() ; mutex.V() ; } while (TRUE);