Critical Section and Critical Resources B. Ramamurthy
Issues in Concurrency Concurrent processes /threads help in improving performance However when they share resources, it is required that the access to the resource is protected from inconsistency and incorrect states. These shared resources are called critical resources and the code accessing the resource is called the critical section. The access to the resource needs to be mutually exclusive among concurrent processes. Semaphores are kernel level primitives to help implement mutual exclusion.
Principles of Concurrency Interleaving and overlapping the execution of processes. Consider two processes P1 and P2 executing the function echo: { input (in, keyboard); out = in; output (out, display); } 11/18/2018
...Concurrency (contd.) P1 invokes echo, after it inputs into in , gets interrupted (switched). P2 invokes echo, inputs into in and completes the execution and exits. When P1 returns in is overwritten and gone. Result: first ch is lost and second ch is written twice. This type of situation is even more probable in multiprocessing systems where real concurrency is realizable thru’ multiple processes executing on multiple processors. Solution: Controlled access to shared resource Protect the shared resource : in buffer; “critical resource” one process/shared code. “critical region” 11/18/2018
Semaphores Semaphore is kernel level primitive for realizing mutual exclusion. Attributes: semaphore value, Operations on semaphore: init, wait, signal Considered an kernel resource, a limited number available: a limited number of instances (objects) of semaphore is allowed. Can easily implement mutual exclusion among any number of processes. Can also be used for synchronization. 11/18/2018
Critical Section of n Processes Shared data: Semaphore mutex; //initially mutex = 1 Process Pi: do { mutex.wait(); critical section mutex.signal(); remainder section } while (1); 11/18/2018
Semaphore Implementation Define a semaphore as a class: class Semaphore { int value; // semaphore value ProcessQueue L; // process queue //operations wait() signal() } In addition, two simple utility operations: block() suspends the process that invokes it. Wakeup() resumes the execution of a blocked process P. 11/18/2018
Semantics of wait and signal Semaphore operations now defined as S.wait(): S.value--; if (S.value < 0) { add this process to S.L; block(); // block a process } S.signal(): S.value++; if (S.value <= 0) { remove a process P from S.L; wakeup(); // wake a process 11/18/2018
Semaphores for CS Semaphore is initialized to 1. The first process that executes a wait() will be able to immediately enter the critical section (CS). (S.wait() makes S value zero.) Now other processes wanting to enter the CS will each execute the wait() thus decrementing the value of S, and will get blocked on S. (If at any time value of S is negative, its absolute value gives the number of processes waiting blocked. ) When a process in CS departs, it executes S.signal() which increments the value of S, and will wake up any one of the processes blocked. The queue could be FIFO or priority queue. 11/18/2018
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. ex: nachos Can implement a counting semaphore using a binary semaphore. 11/18/2018
Semaphore for Synchronization Execute B in Pj only after A executed in Pi Use semaphore flag initialized to 0 Code: Pi Pj A flag.wait() flag.signal() B 11/18/2018
Classical Problems of Synchronization Bounded-Buffer Problem Readers and Writers Problem Dining-Philosophers Problem 11/18/2018
Producer/Consumer problem repeat produce item v; b[in] = v; in = in + 1; forever; Consumer repeat while (in <= out) nop; w = b[out]; out = out + 1; consume w; forever; 11/18/2018
Solution for P/C using Semaphores Producer repeat produce item v; MUTEX.wait(); b[in] = v; in = in + 1; MUTEX.signal(); forever; What if Producer is slow or late? Consumer repeat while (in <= out) nop; MUTEX.wait(); w = b[out]; out = out + 1; MUTEX.signal(); consume w; forever; Ans: Consumer will busy-wait at the while statement. 11/18/2018
P/C: improved solution Producer repeat produce item v; MUTEX.wait(); b[in] = v; in = in + 1; MUTEX.signal(); AVAIL.signal(); forever; What will be the initial values of MUTEX and AVAIL? Consumer repeat AVAIL.wait(); MUTEX.wait(); w = b[out]; out = out + 1; MUTEX.signal(); consume w; forever; ANS: Initially MUTEX = 1, AVAIL = 0. 11/18/2018
P/C problem: Bounded buffer Producer repeat produce item v; while((in+1)%n == out) NOP; b[in] = v; in = ( in + 1)% n; forever; How to enforce bufsize? Consumer repeat while (in == out) NOP; w = b[out]; out = (out + 1)%n; consume w; forever; ANS: Using another counting semaphore. 11/18/2018
P/C: Bounded Buffer solution Producer repeat produce item v; BUFSIZE.wait(); MUTEX.wait(); b[in] = v; in = (in + 1)%n; MUTEX.signal(); AVAIL.signal(); forever; What is the initial value of BUFSIZE? Consumer repeat AVAIL.wait(); MUTEX.wait(); w = b[out]; out = (out + 1)%n; MUTEX.signal(); BUFSIZE.signal(); consume w; forever; ANS: size of the bounded buffer. 11/18/2018
Semaphores - comments Intuitively easy to use. wait() and signal() are to be implemented as atomic operations. Difficulties: signal() and wait() may be exchanged inadvertently by the programmer. This may result in deadlock or violation of mutual exclusion. signal() and wait() may be left out. Related wait() and signal() may be scattered all over the code among the processes. 11/18/2018