Tutorial 3 Sync or sink! presented by: Antonio Maiorano Paul Di Marco
Whats the problem? Example –A cup of coffee –A pourer (producer) –A drinker (consumer) Result: A mess! Drinker(s): while (true){ drink(); } Pourer: while (true){ pour(); }
So, what do we want? Simple protocol: Pourer pours only when cup is empty Drinker drinks only when cup is full Pourer: while (true){ if (!full){ pour(); full = true; } Drinker(s): while (true){ if (full) { drink(); full = false; }
Ok, but… … what if we have two drinkers? Both drinkers can drink at the same time! Drinker 2: while (true){ if (full) { drink(); full = false; } Drinker 1: while (true){ if (full) { drink(); full = false; } Need this to be atomic!
Fine! So how can we do this? Use Semaphores! A semaphore is an object used to synchronize multiple threads. Two functions: –Wait() : willing –Signal() : release the lock Both functions are guaranteed to be atomic
Semaphore Example Clients arriving at a restaurant with only 10 seats. Manager = Semaphore Clients = Threads 10 Seats = 10 Limited resources controlled using the semaphore.
Semaphore Java Code class Semaphore { private int value; Semaphore (int value1) { value = value1; } public synchronized void Wait () { while( value <= 0 ) { try { wait (); } catch (InterruptedException e) { }; } value--; } public synchronized void Signal () { ++value; notify (); }
Using semaphores, we can make sure that only one drinker can ever drink at one time Important: Wait() and Signal() are guaranteed to be atomic! Multiple drinkers? No problem! Drinker i: while (true){ sem.Wait(); drink(); sem.Signal(); } Main: // Semaphore initialization Semaphore cup; cup = new Semaphore(1);
Programming Assignment 2 Overview of the Problems: 1.The run() of Acquire and Release are not critical sections, hence they could leave Block in an inconsistent internal state 2.Acquires are not to run before the Releases complete
To make matters worse… Not only: 1.are the run() functions not mutually exclusive – yields are inserted to force it to go wrong! 2.must the Acquires wait for the Releases to finish – the Acquires are put on the ready queue first! Ready Queue after all threads are created: main > A1 > A2 > R1 > R2
yield() s at crucial junctures These explicit yield() s represent what could be a time-slice context-switch in a real-world environment ++Block.top; ReleaseBlock : yield (); (push)Block.stack[Block.top] = block ; block = Block.stack[Block.top]; AcquireBlock: yield (); (pop) --Block.top;
Semaphore Initial Values Note that the mutex and semaphores are initialized to different values: The mutex is initialized to one to allow only one thread into the critical section at any time The prisem semaphores are initialized to zero to allow no Acquires to run (initially)
Finally - Assignment Solution!!! Yeah right! 1.Use the mutual-exclusion semaphore to make acquire and release atomic 2.Use the two prisem semaphores to enforce the ordering that all ReleaseBlock threads must complete before any AcquireBlock threads can begin.