Process Synchronization Topics: 1.Background 2.The critical-section problem 3.Semaphores 4.Critical Regions 5.Monitors Background Concurrent access to Data may result in data inconsistency To maintain data consistency we should have orderly execution of co-operating processes In the bounded buffer example, what happens I we maintain a counter to keep track of number of items produced common to producer and consumer Bounded Buffer Shared Data: buffer[n],in,out,counter Producer Process: repeat produce an item in nextp while counter==n wait buffer[in]=nextp;in=(in+1)%n;counter++; until false Bounded Buffer (continued) Consumer Process repeat while counter==0 do no-op nextc=buffer[out]; counter--; consume item nextc until false Statements counter++ and counter-- have to be done atomically - no interrupt Critical Section Problem n processes competing to use shared data Each process has a code segment called critical section in which shared data is accessed Issue: When one process is in critical section, no other process is in critical section Structure of Process repeat Critical section Remainder section until false Solution to Critical Section Problem 1. Mutual Exclusion: No two processes are in the critical section at the same time. 2. Progress: If no process is in the critical section and a process wishes to enter, that process cannot be postponed indefinitely 3. Bounded waiting: A bound must exist on the number of times that other processes are allowed to enter their critical sections after a process has made a request to enter its cs and that process is granted. Initial Attempts to solve Problem Only 2 Processes P_0 and P_1 General Structure of Process P_j (other is P_i) repeat entry section; critical section; exit section;remainder section; until false; Processes may share some common variables to synchronize their actions. Algorithm 1 Shared variables: int turn /* turn=j it P_jth turn*/ Process P_j repeat while (turn <> j) wait critical section; turn=I; remainder section; umtil false Algorithm 2 Shared variables: boolean flag[2] /*flag[j]=true means jth turn */ Process P_j entry section: flag[j]=true; while (flag[i]) wait; exit section: flag[j]=false; As before satisfies mutual exclusion but no progress. Algorithm 3 Shared variables: flag[2] , turn; Process P_j entry section: flag[j]=true; turn=j; while (flag[i] and turn==i) wait; exit section: flag[j]=false; Satisfies all three requirements- critical thing to note is turn is a shared variable. Bakery Algorithm Critical Section for n processors Before entering the critical section, process receives a number. Holder of the smallest number enters its critical section. If processes P_i and P_j receive the same number, if i<j, then P_i is served first else p_j is served. Numbering scheme always generates numbers in non-decreasing order. Bakery Algorithm (continued) We use lexicographic order. (a,b) < (c,d) is either a< c or a=c b< d. Shared data: Boolean choosing[n] int number[n] they are initialized to false and 0 resply. Bakery Algorithm (continued) Enter section: choosing[j] = true; number[j]=max of numbers +1 choosing[j]=false; for (k=0;k<n;k++) { while(choosing[k]) wait; while(number[k] <>0) and (number[k],k) <number[j],j) wait; }Leave Section: number[j]=0; Synchronization Hardware Test and modify the content of a word atomically boolean Test-and-set(boolean target){ Test-and-set = target; target = true; } Mutual Exclusion with Testand-set Shared data: boolean lock (initially false) Process P_j: enter section: while (Test-and-lock) wait; leave section: lock = false; Semaphore Synchronization tool that does not require busy wait Semaphore S - integer variable can only be accessed via two indivisible atomic operations wait(s): while (s <=0) no-op; s = s-1; signal(s): s=s+1; Critical section for n processes Shared Variable: semaphore mutex; initial value of mutex=1; entersection: wait(mutex); leavesection: signal(mutex); Semaphore Implementation Semaphore as a struct typedef semaphore struct { int value; ProcessList L } Two Simple operations: block - suspends the process that invokes it wakeup(P) resumes the execution of a blocked process P Semaphore Implementation (continued) Wait(S) : S.value--; if (S.value<0) { add this process to S.l; block;} Signal(S): S.value++; if (S.value<=0) {remove a process from P from S.L; wakeup(P);} Implementation (continued) Wait(S) : S.value--; if (S.value<0) { add this process to S.l; block;} Signal(S): S.value++; if (S.value<=0) {remove a process from P from S.L; wakeup(P);} Semaphore as General Synchronization Tool Execute B in P_j only after A is executed in P_i Use semaphore flag initialized to 0 code P_i: A ; signal(flag) Code P_j: wait(flag); B; Deadlock and Starvation Deadlock - two or more processes are waiting for indefinitely for an event that can be caused by only one of the waiting processes. S and Q are semaphores. P_0: wait(S);wait(Q); ..signal(S);signal(Q); P_1:wait(Q);wait(S);..;signal(Q);signal(S); Starvation: A process may never be removed from the semaphore queue . Two Types of Semaphores •Counting Semaphore - integer value can range over an unrestricted domain •Binary Semaphore - integer value can range from 0 to 1 - easier to implement. •Can implement (simulate) a counting semaphore using a binary semaphore. Implementing S as a binary Semaphore Data Structures: binary_semaphore s1,s2,s3; int c; Initialization s3=s1=1; s2=0; c=initial value of semaphore Implementing S (continued) Wait operation wait(s3);wait(s1);c=c-1; if (c<0) {signal(s1);wait(s2); else {signal(s1);} signal(s3); Signal Operation: wait(s1); c=c+1; if (c<=0) {signal(s2);} signal(s1); Classical Problems of Synchronization Bounded Buffer Problem Readers and writers Problem Dining Philosopher Bounded Buffer Problem Shared Data String Item; String buffer[]; semaphore full,empty,mutex; item nextp,nextc; full=0;empty=n;mutex=1 Producer Process Repeat { produce an item in nextp; wait(empty);wait(mutex); add nextp to buffer; signal(mutex); signal(full); } until false; Consumer Process Repeat { wait(full);wait(mutex); remove an item from buffer in nextc; signal(mutex); signal(empty); consume an item in nextc; } until false; Readers and Writers Problem Shared Data: Semaphore mutex,wrt;(=1) int readcount; Writer Process: wait(wrt); write; signa(wrt); Reader Process Entersection: wait(mutex); readcount++; if (readcount==1) wait(wrt); signal(mutex); leavesection: wait(mutex);readcount-; if(reacount==0) signal(wrt); signal(mutex); Dining Philosopher Problem Shared data: Semaphore chopstick[5](=1); Philosopher_j entersection: wait(chopstick[j]); wait(chopstick[(j+1)%5]); Leavesection: signal(chopstick[j]); signal(chopstick[(j+1)%5]);