Outline • Announcements • Basic Synchronization Principles – continued – Semaphores – Bounded-buffer problem – Read-writer’s problem Announcements • The second quiz will be at the end of today’s class – It will be closed-book and closed-note – It requires to compute average wait time and turnaround time (or something like that) and you need a calculator • Questions regarding grading of homework and programming assignments – Please talk to Yong Chen first to resolve the issues you have – Come to me if the issues cannot be resolved 5/29/2016 COP4610 2 Announcements – cont. • Demonstration of Lab 1 – Please sign up to demonstrate your program if you have not done so – You need to demo your program in LOV 105 D at Yong Chen’s office – You have to demonstrate your program on or before Oct. 28, 2003 5/29/2016 COP4610 3 Interacting Processes - review • Independent process – Cannot affect or be affected by the other processes in the system – Does not share any data with other processes • Interacting process – Can affect or be affected by the other processes – Shares data with other processes – We focus on interacting processes through physically or logically shared memory 5/29/2016 COP4610 4 Bounded-Buffer – review 5/29/2016 COP4610 5 Bounded-Buffer - cont. • Suppose we have one producer and one consumer, the variable counter is 5 – Producer: counter = counter +1 P1: load counter, r1 P2: add r1, #1, r2 P3: store r2, counter – Consumer: counter = counter - 1 C1: load counter, r1 C2: add r1, #-1, r2 C3: store r2, counter 5/29/2016 COP4610 6 Bounded-Buffer - cont. • A particular execution sequence – P1: load counter, r1 – P2: add r1, #1, r2 --- Context switch ---– C1: load counter, r1 – C2: add r1, #-1, r2 – C3: store r2, counter --- Context switch ---– P3: store r2, counter – What is the value of counter? 5/29/2016 COP4610 7 Bounded-Buffer - cont. • A particular execution sequence – C1: load counter, r1 – C2: add r1, #-1, r2 --- Context switch ---– P1: load counter, r1 – P2: add r1, #1, r2 – P3: store r2, counter --- Context switch ---– C3: store r2, counter – What is the value of counter this time? 5/29/2016 COP4610 8 Bounded-Buffer - cont. • A particular execution sequence – C1: load counter, r1 – C2: add r1, #-1, r2 – C3: store r2, counter --- Context switch ---– P1: load counter, r1 – P2: add r1, #1, r2 – P3: store r2, counter --- Context switch ---– What is the value of counter this time? 5/29/2016 COP4610 9 Race Condition • Race condition – When several processes access and manipulate the same data concurrently, there is a race among the processes – The outcome of the execution depends on the particular order in which the access takes place – This is called a race condition 5/29/2016 COP4610 10 Traffic Intersections 5/29/2016 COP4610 11 A Semaphore 5/29/2016 COP4610 12 The Critical-Section Problem • n processes all competing to use some shared data • Each process has code segments, called critical section, in which the shared data is accessed. • Problem – ensure that when one process is executing in its critical section, no other process is allowed to execute in its critical section, called mutual exclusion 5/29/2016 COP4610 13 The Critical-Section Problem – cont. • Structure of process Pi repeat entry section critical section exit section reminder section until false; 5/29/2016 COP4610 14 Requirements for Critical-Section Solutions • Mutual Exclusion. – If process Pi is executing in its critical section, then no other processes can be executing in their critical sections. • Progress – If no process is executing in its critical section and there exist some processes that wish to enter their critical section, then the selection of the processes that will enter the critical section next cannot be postponed indefinitely. • 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 critical section and before that request is granted. 5/29/2016 COP4610 15 Solution through Disabling Interrupts • In a uni-processor system, an interrupt causes the race condition – The scheduler can be called in the interrupt handler, resulting in context switch and data inconsistency 5/29/2016 COP4610 16 Solution through Disabling Interrupts – cont. • Process Pi repeat disableInterrupts(); //entry section critical section enableInterrupts(); //exit section reminder section until false; 5/29/2016 COP4610 17 Solution through Disabling Interrupts – cont. 5/29/2016 COP4610 18 Solution through Disabling Interrupts – cont. • This solution may affect the behavior of the I/O system – Interrupts can be disabled for an arbitrarily long time • The interrupts can be disabled permanently if the program contains an infinite loop in its critical section – User programs are not allowed to enable and disable interrupts directly • However, if the operating system can provide system calls, a user can use the system calls for synchronization – Called semaphores and related system calls 5/29/2016 COP4610 19 Software Attempt to Solve the Problem • Only 2 processes, P1 and P2 • General structure of process Pi (other process Pj) repeat entry section critical section exit section reminder section until false; • Processes may share some common variables to synchronize their actions. 5/29/2016 COP4610 20 Algorithm 1 • Shared variables: – var turn: (0..1); initially turn = 0 – turn - i Pi can enter its critical section • Process Pi repeat while turn i do no-op; critical section turn := j; reminder section until false; • Satisfies mutual exclusion, but not progress 5/29/2016 COP4610 21 Algorithm 2 • Shared variables – var flag: array [0..1] of boolean; initially flag [0] = flag [1] = false. – flag [i] = true Pi ready to enter its critical section • Process Pi repeat flag[i] := true; while flag[j] do no-op; critical section flag [i] := false; remainder section until false; • Satisfies mutual exclusion, but not progress requirement. 5/29/2016 COP4610 22 Algorithm 2 – version 1 • Shared variables – var flag: array [0..1] of boolean; initially flag [0] = flag [1] = false. – flag [i] = true Pi ready to enter its critical section • Process Pi repeat while flag[j] do no-op; flag[i] := true; critical section flag [i] := false; remainder section until false; • Does not satisfy mutual exclusion 5/29/2016 COP4610 23 Algorithm 3 • Combined shared variables of algorithms 1 and 2. • Process Pi repeat flag [i] := true; turn := j; while (flag [j] and turn = j) do no-op; critical section flag [i] := false; remainder section until false; • Meets all three requirements; solves the critical-section problem for two processes. 5/29/2016 COP4610 24 Bakery Algorithm Critical section for n processes • Before entering its critical section, process receives a number. Holder of the smallest number enters the critical section. • If processes Pi and Pj receive the same number, if i < j, then Pi is served first; else Pj is served first. • The numbering scheme always generates numbers in increasing order of enumeration; i.e., 1,2,3,3,3,3,4,5... 5/29/2016 COP4610 25 Bakery Algorithm – cont. • Notation < lexicographical order (ticket #, process id #) – (a,b) < c,d) if a < c or if a = c and b < d – max (a0,…, an-1) is a number, k, such that k ai for i - 0, …, n – 1 • Shared data var choosing: array [0..n – 1] of boolean; number: array [0..n – 1] of integer, Data structures are initialized to false and 0 respectively 5/29/2016 COP4610 26 Bakery Algorithm (Cont.) repeat choosing[i] := true; number[i] := max(number[0], number[1], …, number [n – 1])+1; choosing[i] := false; for j := 0 to n – 1 do begin while choosing[j] do no-op; while number[j] 0 and (number[j],j) < (number[i], i) do no-op; end; critical section number[i] := 0; remainder section until false; 5/29/2016 COP4610 27 Algorithm 4 • Shared variables: – boolean lock; initially lock = FALSE – lock is FALSE Pi can enter its critical section • Process P1 Process P2 repeat while (lock) do no-op; lock = TRUE; critical section lock = FALSE; reminder section until false; repeat while (lock) do no-op; lock = TRUE; critical section lock = FALSE reminder section until false; 5/29/2016 COP4610 28 Busy Wait Condition shared boolean lock = FALSE; shared double balance; Code for p1 Code for p2 COP4610 Interrupt lock = FALSE Interrupt Interrupt 5/29/2016 lock = TRUE p2 p1 /* Acquire the lock */ while(lock) ; lock = TRUE; /* Execute critical sect */ balance = balance - amount; /* Release lock */ lock = FALSE; Blocked at while /* Acquire the lock */ while(lock) ; lock = TRUE; /* Execute critical sect */ balance = balance + amount; /* Release lock */ lock = FALSE; 29 Synchronization Hardware • Test and modify the content of a word atomically (indivisibly) – The procedure cannot be interrupted until it has completed the routine – Implemented as a test-and-set instruction boolean Test-and-Set (boolean target) { boolean tmp tmp = target; target = true; return tmp; } 5/29/2016 COP4610 30 Test and Set Instruction – cont. • TS(m): [Reg_i = memory[m]; memory[m] = TRUE;] Data CC Register Register R3 … m Data CC Register Register … R3 m FALSE Primary Memory (a) Before Executing TS 5/29/2016 FALSE =0 TRUE Primary Memory (b) After Executing TS COP4610 31 Mutual Exclusion with Test-and-Set • Shared data: boolean lock ; – Initially false • Process Pi repeat while Test-and-Set (lock) do no-op; critical section lock = false; remainder section until false; 5/29/2016 COP4610 32 Semaphores • Semaphore S – integer variable • can only be accessed via two indivisible (atomic) operations – As if they were a single machine instruction wait (S): while S 0 do no-op; S := S – 1; signal (S): S := S + 1; 5/29/2016 COP4610 33 Two Types of Semaphores • Counting semaphore – integer value can range over an unrestricted domain. • Binary semaphore – integer value can range only between 0 and 1 – Can be simpler to implement. • Can implement a counting semaphore S using binary semaphores. 5/29/2016 COP4610 34 Example: Critical Section of n Processes • Shared variables – var mutex : semaphore – initially mutex = 1 • Process Pi repeat wait(mutex); critical section signal(mutex); remainder section until false; 5/29/2016 COP4610 35 Shared Account Balance Problem Proc_0() { . . . /* Enter the CS */ P(mutex); balance += amount; V(mutex); . . . } proc_1() { . . . /* Enter the CS */ P(mutex); balance -= amount; V(mutex); . . . } semaphore mutex = 1; fork(proc_0, 0); fork(proc_1, 0); 5/29/2016 COP4610 36 Sharing Two Variables proc_A() { while(TRUE) { <compute section A1>; update(x); /* Signal proc_B */ V(s1); <compute section A2>; /* Wait for proc_B */ P(s2); retrieve(y); } } proc_B() { while(TRUE) { /* Wait for proc_A */ P(s1); retrieve(x); <compute section B1>; update(y); /* Signal proc_A */ V(s2); <compute section B2>; } } semaphore s1 = 0; semaphore s2 = 0; fork(proc_A, 0); fork(proc_B, 0); 5/29/2016 COP4610 37 Bounded Buffer Problem Empty Pool Producer Consumer Full Pool 5/29/2016 COP4610 38 Bounded Buffer Problem – cont. producer() { consumer() { buf_type *next, *here; buf_type *next, *here; while(TRUE) { while(TRUE) { produce_item(next); /* Claim full buffer */ /* Claim an empty */ P(full); P(empty); P(mutex); P(mutex); here = obtain(full); here = obtain(empty); V(mutex); V(mutex); copy_buffer(here, next); copy_buffer(next, here); P(mutex); P(mutex); release(here, emptyPool); release(here, fullPool); V(mutex); V(mutex); /* Signal an empty buffer */ /* Signal a full buffer */ V(empty); V(full); consume_item(next); } } } } semaphore mutex = 1; semaphore full = 0; /* A general (counting) semaphore */ semaphore empty = N; /* A general (counting) semaphore */ buf_type buffer[N]; fork(producer, 0); fork(consumer, 0); 5/29/2016 COP4610 39 Bounded Buffer Problem – cont. producer() { consumer() { buf_type *next, *here; buf_type *next, *here; while(TRUE) { while(TRUE) { produce_item(next); /* Claim full buffer */ /* Claim an empty */ P(full); P(empty); P(mutex); P(mutex); here = obtain(full); here = obtain(empty); V(mutex); V(mutex); copy_buffer(here, next); copy_buffer(next, here); P(mutex); P(mutex); release(here, emptyPool); release(here, fullPool); V(mutex); V(mutex); /* Signal an empty buffer */ /* Signal a full buffer */ V(empty); V(full); consume_item(next); } } } } semaphore mutex = 1; semaphore full = 0; /* A general (counting) semaphore */ semaphore empty = N; /* A general (counting) semaphore */ buf_type buffer[N]; fork(producer, 0); fork(consumer, 0); 5/29/2016 COP4610 40 Bounded Buffer Problem – cont. consumer() { producer() { buf_type *next, *here; buf_type *next, *here; while(TRUE) { while(TRUE) { /* Claim full buffer */ produce_item(next); P(mutex); /* Claim an empty */ P(full); P(empty); here = obtain(full); P(mutex); V(mutex); here = obtain(empty); copy_buffer(here, next); V(mutex); P(mutex); copy_buffer(next, here); release(here, emptyPool); P(mutex); V(mutex); release(here, fullPool); /* Signal an empty buffer */ V(mutex); V(empty); /* Signal a full buffer */ consume_item(next); V(full); } } } } semaphore mutex = 1; semaphore full = 0; /* A general (counting) semaphore */ semaphore empty = N; /* A general (counting) semaphore */ buf_type buffer[N]; fork(producer, 0); fork(consumer, 0); 5/29/2016 COP4610 41 Readers-Writers Problem Writers Readers 5/29/2016 COP4610 42 Readers-Writers Problem (2) Writer Writer Writer Writer Writer Writer Writer Reader Reader Reader Reader Reader Reader Reader Reader Shared Resource 5/29/2016 COP4610 43 Readers-Writers Problem (3) Writer Writer Writer Writer Writer Writer Writer Reader Reader Reader Reader Reader Reader Reader Reader Shared Resource 5/29/2016 COP4610 44 Readers-Writers Problem (4) Reader Reader Reader Reader Reader Reader Reader Reader Writer Writer Writer Writer Writer Writer Writer Shared Resource 5/29/2016 COP4610 45 Readers-writers with active readers 5/29/2016 COP4610 46 First and Second Policy 5/29/2016 COP4610 47 First Solution reader() { writer() { while(TRUE) { while(TRUE) { <other computing>; <other computing>; P(mutex); P(writeBlock); readCount++; /* Critical section */ if(readCount == 1) access(resource); P(writeBlock); V(writeBlock); V(mutex); } /* Critical section */ } access(resource); P(mutex); readCount--; •First reader competes with writers if(readCount == 0) V(writeBlock); •Last reader signals writers V(mutex); } } resourceType *resource; int readCount = 0; semaphore mutex = 1; semaphore writeBlock = 1; fork(reader, 0); fork(writer, 0); 5/29/2016 COP4610 48 First Solution (2) reader() { while(TRUE) { <other computing>; P(mutex); readCount++; if(readCount == 1) P(writeBlock); V(mutex); /* Critical section */ access(resource); P(mutex); readCount--; if(readCount == 0) V(writeBlock); V(mutex); } } resourceType *resource; int readCount = 0; semaphore mutex = 1; semaphore writeBlock = 1; fork(reader, 0); fork(writer, 0); 5/29/2016 writer() { while(TRUE) { <other computing>; P(writeBlock); /* Critical section */ access(resource); V(writeBlock); } } •First reader competes with writers •Last reader signals writers •Any writer must wait for all readers •Readers can starve writers •“Updates” can be delayed forever •May not be what we want COP4610 49 Writer Precedence reader() { while(TRUE) { <other computing>; P(readBlock); P(mutex1); readCount++; if(readCount == 1) P(writeBlock); V(mutex1); V(readBlock); 4 2 access(resource); P(mutex1); readCount--; if(readCount == 0) V(writeBlock); V(mutex1); 1 writer() { while(TRUE) { <other computing>; P(mutex2); writeCount++; if(writeCount == 1) P(readBlock); 3 V(mutex2); P(writeBlock); access(resource); V(writeBlock); P(mutex2) writeCount--; if(writeCount == 0) V(readBlock); V(mutex2); } } } } int readCount = 0, writeCount = 0; semaphore mutex = 1, mutex2 = 1; semaphore readBlock = 1, writeBlock = 1, writePending = 1; fork(reader, 0); 5/29/2016 COP4610 50 fork(writer, 0); Writer Precedence (2) reader() { writer() { while(TRUE) { while(TRUE) { <other computing>; <other computing>; P(mutex2); 4 P(writePending); P(readBlock); writeCount++; P(mutex1); if(writeCount == 1) readCount++; P(readBlock); 3 if(readCount == 1) V(mutex2); 2 P(writeBlock); P(writeBlock); V(mutex1); access(resource); V(readBlock); V(writeBlock); V(writePending); P(mutex2) access(resource); writeCount--; 1 P(mutex1); if(writeCount == 0) readCount--; V(readBlock); if(readCount == 0) V(mutex2); V(writeBlock); } V(mutex1); } } } int readCount = 0, writeCount = 0; semaphore mutex = 1, mutex2 = 1; semaphore readBlock = 1, writeBlock = 1, writePending = 1; fork(reader, 0); 5/29/2016 COP4610 51 fork(writer, 0); The Sleepy Barber • Barber can cut one person’s hair at a time • Other customers wait in a waiting room Entrance to Waiting Room (sliding door) Shop Exit Entrance to Barber’s Room (sliding door) Waiting Room 5/29/2016 COP4610 52 Sleepy Barber customer() { while(TRUE) { customer = nextCustomer(); if(emptyChairs == 0) continue; P(chair); P(mutex); emptyChairs--; takeChair(customer); V(mutex); V(waitingCustomer); } } barber() { while(TRUE) { P(waitingCustomer); P(mutex); emptyChairs++; takeCustomer(); V(mutex); V(chair); } } semaphore mutex = 1, chair = N, waitingCustomer = 0; int emptyChairs = N; fork(customer, 0); fork(barber, 0); 5/29/2016 COP4610 53 Implementing Semaphores • Minimize effect on the I/O system • Processes are only blocked on their own critical sections (not critical sections that they should not care about) • If disabling interrupts, be sure to bound the time they are disabled 5/29/2016 COP4610 54 Implementing Semaphores Using Interrupts class semaphore { int value; public: semaphore(int v = 1) { value = v;}; P(){ disableInterrupts(); while(value == 0) { enableInterrupts(); disableInterrupts(); } value--; enableInterrupts(); }; V(){ disableInterrupts(); value++; enableInterrupts(); }; }; 5/29/2016 COP4610 55 Using the TS Instruction boolean s = FALSE; . . . while(TS(s)) ; <critical section> s = FALSE; . . . 5/29/2016 semaphore s = 1; . . . P(s) ; <critical section> V(s); . . . COP4610 56 Implementing the General Semaphore struct semaphore { int value = <initial value>; boolean mutex = FALSE; boolean hold = TRUE; }; shared struct semaphore s; P(struct semaphore s) { while(TS(s.mutex)) ; s.value--; if(s.value < 0) ( s.mutex = FALSE; while(TS(s.hold)) ; } else s.mutex = FALSE; } 5/29/2016 V(struct semaphore s) { while(TS(s.mutex)) ; s.value++; if(s.value <= 0) ( while(!s.hold) ; s.hold = FALSE; } s.mutex = FALSE; } COP4610 57 Semaphore Implementation – cont. • Semaphores implemented using interrupt disabling and test-and-set require busy waiting – This type of semaphores is often called spinlock 5/29/2016 COP4610 58 Semaphore Implementation – cont. • Define a semaphore as a structure typedef struct { int value; queue L; } semaphore; • Assume two simple operations: – block suspends the process that invokes it. – wakeup(P) resumes the execution of a blocked process P. 5/29/2016 COP4610 59 Semaphore Implementation - cont. • Semaphore operations now defined as P(S): S.value = S.value – 1; if S.value < 0 then begin add this process to S.L; block; end; 5/29/2016 COP4610 60 Semaphore Implementation - cont. V(S): S.value = S.value + 1; if S.value 0 then begin remove a process P from S.L; wakeup(P); end; 5/29/2016 COP4610 61 Semaphore Implementation - cont. • Semaphores are resources in the above implementation 5/29/2016 COP4610 62 Issues Using Semaphores • Deadlock – two or more processes are waiting indefinitely for an event that can be caused by only one of the waiting processes. • Let S and Q be two semaphores initialized to 1 P0 P(S); P(Q); V(S); V(Q) P1 P(Q); P(S); V(Q); V(S); • Starvation – indefinite blocking – A process may never be removed from the semaphore queue in which it is suspended. 5/29/2016 COP4610 63 Active and Passive Semaphores • There is a subtle problem related to semaphore implementation – Bounded waiting may not be satisfied • when the passive V operation is used where the implementation increments the semaphore with no opportunity for a context switch 5/29/2016 COP4610 64 Active vs. Passive Semaphores – cont. • A process can dominate the semaphore – Performs V operation, but continues to execute – Performs another P operation before releasing the CPU – Called a passive implementation of V 5/29/2016 COP4610 65 Active and Passive Semaphores – cont. 5/29/2016 COP4610 66 Active and Passive Semaphores – cont. • Active semaphores – In contrast to passive semaphores, a yield or similar procedure will be called after incrementing the semaphore for a context switch in a V operation implementation • Changes semantics of semaphore! – Cause people to rethink solutions 5/29/2016 COP4610 67 Summary • Processes that share data need to be synchronized – Otherwise, a race condition may exist • Semaphores are the basic mechanism underlying synchronization and can be used to solve different synchronization problems – Critical section problem – Bounded-buffer problem – Readers-writers problem 5/29/2016 COP4610 68 Session Information • COP 4610 / CGS 5765 – – – – – Section Section 6 Section 2 Section 3 Section 4 Recitation time 08:00-08:55 AM 09:05-09:55 AM 11:15AM -12:05 PM 12:20-1:10 PM • For problem 3c., for round-robin scheduling, we assume a newly arrived process will be inserted at the end of the ready queue at the time it arrives 5/29/2016 COP4610 69 5/29/2016 COP4610 70