Process Synchronization Part II, Modified by M.Rebaudengo - 2013 Silberschatz, Galvin and Gagne 2009
Classical Problems of Synchronization Consumer/Producer with Bounded-Buffer Problem s and s Problem Dining-Philosophers Problem The three problems are important, because they are examples for a large class of concurrency-control problems. used for testing nearly every newly proposed synchronization scheme. Semaphores are used for synchronization in our solutions. 6.2 Silberschatz, Galvin and Gagne 2009
Consumer/Producer with Bounded Buffer Two processes (at least) use a shared buffer in memory The buffer is finite (i.e. bounded) The producer writes on the buffer and the consumer reads from it A full buffer stops the producer An empty buffer stops the consumer buffer of size n producer consumer 6.3 Silberschatz, Galvin and Gagne 2009
Bounded buffer (II) Producer Producer Consumer Consumer Consumer 6.4 Silberschatz, Galvin and Gagne 2009
Bounded-Buffer Problem (cont.) Correctness Constraints: Consumer must wait for producer to fill buffers, if empty (scheduling constraint) Producer must wait for consumer to empty buffers, if full (scheduling constraint) Only one thread can manipulate buffer queue at a time (mutual exclusion) 3 Semaphores: mutex full empty. 6.5 Silberschatz, Galvin and Gagne 2009
Bounded-Buffer Problem Shared data: semaphore full, empty, mutex; Initially: full = 0 empty = n mutex = 1 # of full cells # of empty cells 6.6 Silberschatz, Galvin and Gagne 2009
Bounded-Buffer Problem append(v) { b[in] = v; in = (in+1) % N; take() { w = b[out]; out =(out+1) % N; return w; append(v): a function, used by the producer, to add an item v to the buffer in the next position (b[in]).. take():a function, used by the consumer, to remove the next item from the buffer (b[out]) to prepare for consuming. append and take are critical sections because they use the shared buffer. 6.7 Silberschatz, Galvin and Gagne 2009
Bounded Buffer Problem (Cont.) Producer do { produce item 0, iff no wait(empty); cells wait(mutex); append (item) ; signal(mutex); signal(full); while (1); add one item Consumer do { wait(full); wait(mutex); item = take(); signal(mutex); signal(empty); consume the item while (1); 0, iff no item free one cell 6.8 Silberschatz, Galvin and Gagne 2009
Bad solution Producer do { produce item wait(empty); wait(mutex); append (item) ; signal(mutex); signal(full); while (1); Consumer do { wait(mutex); wait(full); item = take(); signal(mutex); signal(empty); consume the item while (1); 6.9 Silberschatz, Galvin and Gagne 2009
Order matters The order of the two P( ) operations is very important Neither the producer or the consumer should request exclusive access to the buffer before being sure they can perform the operation they have to perform The order of the two V( ) operations does not matter 6.10 Silberschatz, Galvin and Gagne 2009
s-s Problem s s 6.11 Silberschatz, Galvin and Gagne 2009
s-s Problem Shared Resource It s logically acceptable for an arbitrary number of readers to access the shared resource at the same time but if a writer is accessing the shared resource, it s unsafe to allow any other reader or writer to access it at the same time. 6.12 Silberschatz, Galvin and Gagne 2009
s-s Problem OK Shared Resource OK Shared Resource Operating Computer Science System Dept Concepts Va Tech September 8 th Edition 2006 6.13 Silberschatz, Galvin 2006 McQuain and Gagne & Ribbens 2009
s-s Problem Not OK Shared Resource 6.14 Silberschatz, Galvin and Gagne 2009
s-s Problem A data set is shared among a number of concurrent processes s only read the data set; they do not perform any updates. Many readers may access a database without fear of data corruption (interference) s can both read and write Problem allow multiple readers to read at the same time. Only one single writer can access the shared data at the same time s Shared object, e.g. file s a writer must have exclusive access W1 W2 R1 R2 s can share with any other reader but not a writer 6.15 Silberschatz, Galvin and Gagne 2009
s-s Problem There can be only one writer at a time, but there can be many simultaneous readers. Each writer has exclusive access. Options: 1. readers wait only if a writer has already obtained access permission (no reader will be kept waiting if there are writers waiting) 2. s have priority, start right away, temporarily blocking readers. Once a writer is ready, that writer has to perform its write as soon as possible, after old readers (or writer) are completed. Thus, if a writer is waiting to access the object, no new readers may start reading. A solution to either problem may result in starvation. with reader precedence: s with writer precedence: s. 6.16 Silberschatz, Galvin and Gagne 2009
s-s Problem Shared Data: Data set Integer readcount initialized to 0 (current number of readers) Semaphore mutex initialized to 1 (protect readcount updates) Semaphore wrt initialized to 1 (protect exclusion of writers) 6.17 Silberschatz, Galvin and Gagne 2009
A solution for the first problem The mutex (init =1) semaphore is used to ensure mutual exclusion when the variable readcount is updated. Readcount (init =0) keeps track of how many processes are currently reading the object. The wrt (init =1) semaphore functions as a mutual exclusion semaphore for the writers. It also is used by the first or last reader that enters or exits the critical section. It is not used by the readers who enter or exit while other processes are in their critical sections. 6.18 Silberschatz, Galvin and Gagne 2009
s-s Problem #1 Process while(true) { wait(wrt); writing is performed signal(wrt); 6.19 Silberschatz, Galvin and Gagne 2009
s-s Problem #1 Process mutex protects readcount updates mutex protects readcount updates while(true) { wait(mutex); readcount++; if (readcount == 1) First in wait(wrt); signal(mutex); reading is performed wait(mutex); readcount--; if (readcount == 0) signal(wrt); signal(mutex); The last reader sends a signal and, a reader or a writer, may be scheduled 6.20 Silberschatz, Galvin and Gagne 2009
The s/s Problem #1 If multiple writers seek to write, then the write semaphore wrt provides mutual exclusion If the 1st reader tries to read while a writer is writing, then the 1st reader blocks on wrt if subsequent readers try to read while a writer is writing, they block on wrt If the 1st reader reads and there are no writers, then 1st reader grabs the write lock and continues reading, eventually releasing the write lock when done reading if a writer tries to write while the 1st reader is reading, then the writer blocks on the write lock wrt if a 2nd or any subsequent reader tries to read while the 1st reader is reading, then it falls through and is allowed to read. s can starve writers Updates can be delayed forever May not be what we want. 6.21 Silberschatz, Galvin and Gagne 2009
s-s Problem #1: comments If a writer is in the CS and n readers are waiting, then one is queued on wrt the other n-1 are queued on mutex When a writer executes signal(wrt), either the waiting readers or a single writer are resumed. The selection is made by the scheduler. 6.22 Silberschatz, Galvin and Gagne 2009
s-s Problem #2: writer precedence reader() { while(true) { <other computing>; P(readBlock); P(mutex1); readcount++; if(readcount == 1) P(writeBlock); V(mutex1); V(readBlock); access(resource); P(mutex1); readcount--; if(readcount == 0) V(writeBlock); V(mutex1); int readcount = 0, writecount = 0; semaphore mutex1 = 1, mutex2 = 1; semaphore readblock = 1, writeblock = 1; writer() { while(true) { <other computing>; P(mutex2); writecount++; if(writecount == 1) P(readBlock); V(mutex2); P(writeBlock); access(resource); V(writeBlock); P(mutex2) writecount--; if(writecount == 0) V(readBlock); V(mutex2); 6.23 Silberschatz, Galvin and Gagne 2009
s-s Problem #2: writer precedence reader() { while(true) { <other computing>; 1 2 P(readBlock); P(mutex1); readcount++; if(readcount == 1) P(writeBlock); V(mutex1); V(readBlock); access(resource); P(mutex1); readcount--; if(readcount == 0) V(writeBlock); V(mutex1); int readcount = 0, writecount = 0; semaphore mutex1 = 1, mutex2 = 1; semaphore readblock = 1, writeblock = 1; writer() { while(true) { <other computing>; P(mutex2); writecount++; if(writecount == 1) 3 P(readBlock); V(mutex2); P(writeBlock); access(resource); V(writeBlock); P(mutex2) writecount--; if(writecount == 0) V(readBlock); V(mutex2); First does a P(readBlock) to block any new readers 6.24 Silberschatz, Galvin and Gagne 2009
s-s Problem #2: writer precedence reader() { while(true) { <other computing>; 4 1 2 P(readBlock); P(mutex1); readcount++; if(readcount == 1) P(writeBlock); V(mutex1); did a P(readBlock) V(readBlock); Next reader is blocked because the first writer access(resource); P(mutex1); readcount--; if(readcount == 0) V(writeBlock); V(mutex1); int readcount = 0, writecount = 0; semaphore mutex1 = 1, mutex2 = 1; semaphore readblock = 1, writeblock = 1; writer() { while(true) { <other computing>; P(mutex2); writecount++; if(writecount == 1) P(readBlock); V(mutex2); 3 P(writeBlock); access(resource); V(writeBlock); P(mutex2) writecount--; if(writecount == 0) V(readBlock); V(mutex2); blocks on P(writeBlock) 6.25 Silberschatz, Galvin and Gagne 2009
s-s Problem #2: writer precedence reader() { while(true) { <other computing>; 4 2 P(readBlock); P(mutex1); readcount++; if(readcount == 1) P(writeBlock); V(mutex1); V(readBlock); access(resource); P(mutex1); readcount--; if(readcount == 0) V(writeBlock); V(mutex1); Last reader signals 1 int readcount = 0, writer writecount to begin = 0; semaphore mutex1 = 1, mutex2 = 1; semaphore readblock = 1, writeblock = 1; writer() { while(true) { <other computing>; P(mutex2); writecount++; if(writecount == 1) P(readBlock); V(mutex2); 3 P(writeBlock); access(resource); V(writeBlock); P(mutex2) writecount--; if(writecount == 0) V(readBlock); V(mutex2); 6.26 Silberschatz, Galvin and Gagne 2009
s-s Problem #2: writer precedence reader() { while(true) { <other computing>; 4 P(readBlock); P(mutex1); readcount++; Any new if(readcount reader must == 1) P(writeBlock); wait until the last writer V(mutex1); V(readBlock); signals access(resource); P(mutex1); readcount--; if(readcount == 0) V(writeBlock); V(mutex1); int readcount = 0, writecount = 0; semaphore mutex1 = 1, mutex2 = 1; semaphore readblock = 1, writeblock = 1; writer() { while(true) { <other computing>; P(mutex2); writecount++; if(writecount == 1) P(readBlock); V(mutex2); 5 P(writeBlock); 3 access(resource); V(writeBlock); P(mutex2) writecount--; if(writecount == 0) Any new writer has priority over any waiting reader but is stuck until first writer is finished V(readBlock); V(mutex2); 6.27 Silberschatz, Galvin and Gagne 2009
s-s Problem #2: comments Any writer must wait for current readers to finish Any writer has priority over any new readers The writers can starve readers Reads can be delayed forever May not be what we want. 6.28 Silberschatz, Galvin and Gagne 2009
Dining Philosophers Problem Model allocating several resources among several processes. The Dining Philosophers Problem stated (1965) as follows. Five philosophers are seated around a circular table Five plates of foods (spaghetti in the original Dijkstra's paper ) Five chopstics (forks) Between each pair of plates there is one fork Philosophers spend time eating and thinking: Philosophers think about the world and ignore food When they want to eat, need two forks Pick up one and then the other one. 6.29 Silberschatz, Galvin and Gagne 2009
Each philosopher is modeled with a thread while(true) { Think(); Grab first fork; Grab second fork; Eat(); Put down first fork; Put down second fork; 6.30 Silberschatz, Galvin and Gagne 2009
Philosopher Process Shared data spaghetti (data set) Semaphore fork [5] initialized to 1 Philosopher i: repeat wait( fork[ i]); // get left fork wait( fork[ i+1 mod 5]); // right fork eat signal( fork[ i]); // return left fork signal( fork[ i+1 mod 5]); // return right fork think until false; 6.31 Silberschatz, Galvin and Gagne 2009
Simplest Example of Deadlock In this algorithm, take-fork waits until the specified fork is available. The solution is wrong because If all five philosophers take their left fork simultaneously there will be deadlock (i.e., processes stay blocked forever). Thread 0 Interleaving Thread 1 P(R1) P(R2) V(R1) V(R2) P(R1) P(R2) P(R1) waits P(R2) waits P(R2) P(R1) V(R2) V(R1) 6.32 Silberschatz, Galvin and Gagne 2009
Remedies Teach philosophers to eat spaghetti with 1 fork! Give them another fork Allow at most 4 philosophers at the table Use asymmetry odd philosophers pick up left first, while even philosophers pick up right first Check to see if both forks are available, then pick them up Besides deadlock, any satisfactory solution to the DP problem must avoid the problem of starvation. 6.33 Silberschatz, Galvin and Gagne 2009
Admit only 4 philosophers Then 1 philosopher can always eat when the other 3 are holding 1 fork Hence, we can use another semaphore T that would limit at 4 the number of philosophers sitting at the table Process Pi: repeat think; wait(t); wait(fork[i]); wait(fork[i+1 mod 5]); eat; signal(fork[i+1 mod 5]); signal(fork[i]); signal(t); forever 6.34 Silberschatz, Galvin and Gagne 2009
Working towards a solution #define N 5 Philosopher() { while(true) { Think(); take_fork(i); take_fork((i+1)% N); Eat(); put_fork(i); put_fork((i+1)% N); take_forks(i) put_forks(i) 6.35 Silberschatz, Galvin and Gagne 2009
Working towards a solution #define N 5 Philosopher() { while(true) { Think(); take_forks(i); Eat(); put_forks(i); 6.36 Silberschatz, Galvin and Gagne 2009
Picking up forks initial values 0 0 0 0 0 int state[n] semaphore mutex = 1 semaphore sem[i] take_forks(int i) { wait(mutex); state [i] = HUNGRY; test(i); signal(mutex); wait(sem[i]); // only called with mutex set! test(int i) { if (state[i] == HUNGRY && state[left]!= EATING && state[right]!= EATING){ state[i] = EATING; signal(sem[i]); 6.37 Silberschatz, Galvin and Gagne 2009
Putting down forks int state[n] semaphore mutex = 1 semaphore sem[i] put_forks(int i) { wait(mutex); state [i] = THINKING; test(left); test(right); signal(mutex); // only called with mutex set! test(int i) { if (state[i] == HUNGRY && state[left]!= EATING && state[right]!= EATING){ state[i] = EATING; signal(sem[i]); 6.38 Silberschatz, Galvin and Gagne 2009