650 likes | 1.71k Views
Dining-Philosophers Problem. Shared data fork[5]: semaphore; initialized to 1 . Relevance. Dining philosopher’s problem models a large class of concurrency-control problems Highlights need for freedom from deadlock and starvation
E N D
Dining-Philosophers Problem Shared data fork[5]: semaphore; initialized to 1
Relevance • Dining philosopher’s problem models a large class of concurrency-control problems • Highlights need for freedom from deadlock and starvation • Early versions of FreeBSD failed to address this problem right, and hence parallelism was severely impacted
Simple and Obvious solution do { wait(fork[i]) wait(fork[(i+1) % 5]) … eat … signal(fork[i]); signal(fork[(i+1) % 5]); … think … } while (1); Problems with the solution?
Problems with the solution • Not dead-lock free • A Second Alternative • Use a back-off time • Problem? • A Third Alternative • Use a random back-off time • Problem? • Yet Another Alternative • Binary Semaphore • FreeBSD • Problem?
Tanenbaum’s solution (1/4) • Datastructures • 3 States of phil are defined • THINKING, HUNGRY, EATING • 2 Semaphores • mutex – binary semaphore • Provides mutual exclusion in critical region • Initial value = 1 • s[5] – array of semaphores • One semaphore per phil • Initial value=0 • Left Neighbor of ‘i’ • (i+4)%5 • Right Neighbor of ‘i’ • (i+1)%5
Tanenbaum’s solution (2/4) Philosopher process 'i‘ ------------------------------ while(1) { think(); take_forks(i); /* Acquire both the forks or block */ eat(); put_forks(i); /* Put back both the forks on table */ }
Tanenbaum’s solution (3/4) Philosopher 'i' taking the fork --------------------------------------- wait(mutex); /* Enter critical section */ state[i]='HUNGRY'; test(i); /* Try to acquire 2 forks */ signal(mutex); /* Exit critical section */ wait(s[i]); /* Block if forks were not acquired */ Test procedure --------------------- Test(i) { if ( state[i] == HUNGRY && state[(i+4)%5] != EATING && state[(i+1)%5] != EATING ) { state[i] = EATING; signal(s[i]); } }
Tanenbaum’s solution (4/4) Philosopher 'i' putting down the fork ------------------------------------------------- wait(mutex); /* Enter critical section */ state[i]='THINKING'; /* Phil has finished eating */ test( (i+4)%5 ); /* See if left neighbour can now eat */ test( (i+1)%5 ); /* See if right neighbour can now eat */ signal(mutex); /* Exit critical section */ This solution is deadlock free Provides for maximum parallelism when extended to any arbitrary ‘N’
Monitors • Why Monitors? • Semaphores demand strict sequencing which are difficult to implement even in relatively simple scenarios • Monitors provide high level constructs that help write correct programs • Monitor is a collection of procedures, variables and data structures grouped together in a special kind of module • Processes may call procedures in a monitor but cannot directly access the data internal to a monitor from procedures declared outside a monitor (private data and public methods as in Java)
Bounded buffer Var f,e,s :semaphore (:=0); (In the begning s=1,f=0,e=n) Producer Begin repeat produce; wait (e) wait(s) append; Signal(s); signal (f); forever End; Consumer Begin repeat wait(f); wait(s); take; signal(s); signal (e); consume; forever End;
Monitors – General Structure monitor monitor-name { //shared variable declarations procedure P1(...){ ... } procedure p2(...){ ... } ….. procedure pn(...){ ... } initialisation code(...){ ... } }//end of monitor
Monitors • How do monitors achieve mutual exclusion? • Only one process is active in a monitor at any instant • Compiler handles calls to monitor procedures differently from other procedure calls • Compiler implements the mutual exclusion • Why is this an advantage over semaphores?? • How to code monitors? • Just turn each critical region into a monitor procedure
Condition variables • Why need them? • Provide means by which processes can block when they cannot proceed in a monitor • Condition variables are special variables that can be declared only inside a monitor • Condition variables have two operations associated with them – wait and signal
Wait and signal • If a process calls wait on a condition variable, it will get blocked until some other process calls signal on the same condition variable • If a process calls signal on a condition variable, it will unblock all processes that were waiting on the same condition variable • Note: wait releases the lock on the monitor
How is this different from Semaphore? • Condition variables are not counters • If a condition variable is signalled with no one waiting on it, the signal is lost
Condition variables - Usage monitor Signal{ private: intchk=0; condition cond; public: void SendSignal(void){ chk=1; signal(cond); } void WaitForSignal(void){ if( !chk) wait(cond); } } int main() { //signal sender // do something Signal.SendSignal(); //continue on } int main(){ //signal receiver //do something Signal.WaitForSignal(); //continue on }
What happens after a signal? • Hoare’s Method • Run the newly awakened process • Hansen’s Method • Process doing the signal must exit monitor immediately – how? • If signal is on a condition variable on which several processes are waiting? • Third solution • Signaler runs and allow waiting process to start running only after signaler exits monitor
Implementing Monitors • How does the compiler implement a monitor? • Using Semaphores (one option)