Review: Monitor Semantics If P does X.wait() and later Q does X.signal(): – Hoare Semantics: • Q blocks yielding the monitor immediately to P – Mesa Semantics: • Q continues. P becomes runnable. P will eventually run, but only after Q exits – Brinch Hanson: • Q can only signal as it exits, so P will run next. – Java synchrnoized methodsj: • Only one reason to wait can exist. Notify() (the Java equivalent of signal() ) makes the blocked thread runnable as with Mesa semantics – it will run at a later time, after Q exits the monitor. Mesa vs. Hoare MONITOR prod-con { struct something buffer[n]; int in=0, out=0; int count=0; condition=full, empty; entry void add_item(data) { /* Mesa semantics require a while – why? Bounded wait? */ if/while (count==n) full.wait(); buffer[in] = data; in = (in + 1) % n; count++; empty.signal(); } /* continued */ Mesa vs. Hoare (cont.) /* continued */ entry remove_item (data_ptr) { /* Mesa semantics require a while – why? Bounded wait? */ if/while (count == n) empty.wait(); *data_ptr = buffer[out]; out = (out + 1) % n; count--; full.signal(); } Dining philosophers Rules P1 •Philosophers alternate between thinking and eating P0 •2 chopsticks are required to eat Rice P2 P4 •Philosophers never grab with both hands – they reach for one chopstick at a time. •A philosopher is too polite to steal a chopstick from a colleague •The philosophers cannot be allowed to starve P3 •More than one philosopher should be able to eat at a time (no token ring!) Dining Philosophers: Approach 1 #define left(i) (i) #define right(i) ((i-1) % 5) MONITOR fork { int avail[5] = {2, 2, 2, 2, 2 }; /* forks available to each phil */ condition hungry[5]; entry pickup_fork (int phil) { if (avail[i] != 2) ready[i].wait(); avail[left(I)]--; avail[right(I)]--; } entry putdown_fork (int phil) { avail[left(i)]++: avail[right(i)]++; if (avail[left(i)] == 2) ready[left(i).signal(); if (avail[right(i)] == 2) ready[right(i)].signal(); } } Why Starvation? • Each philosopher is waiting for a different condition, so we can’t ensure fairness. • The solution is simple: all philosophers should wait for the same condition, such as their turn in a common queue. Dining Philosophers: Approach 2 Semaphore chopstick[5] = { 1, 1, 1, 1, 1 }; while (1) { P(chopstick[i]); P(chopstick[(i+1) % 5]); <<< eat >>> V(chopstick[i]); V(chopstick[(i +1) % 5]); <<< think >>> } What is Deadlock? First, let’s define a resource: • Resources are an abstraction of any reason to wait. • Resources come in different types and we can have different numbers of each type. • A process/thread can acquire a resource, use it, and then fee it. • In this context, resources are unshareable – they are serially reusable. Now let’s formally define deadlock: • The condition that arises when there exists a set of processes (or threads) such that each process holds a resource that another process in the set is waiting to acquire. The situation forces all processes in the set to wait forever. Why Deadlock? Deadlock can occur if these conditions are satisfied: • Mutual exclusion – at least one resource must be held by a process. • Hold and wait – at least one process hold a resource while it is waiting for another resource. • No preemption – one process can’t take another process’s resources in order to make progress (nor can the OS) • Circular wait – there exists a circular chain of processes, each of which is waiting for a resource held by the next process in the chain. Simple Defense: Serialization One simple defense against deadlock is to serialize the request of resources. 1. Enumerate all of the resources, giving each a number. 2. Require that all processes request resources in the order of this enumeration. That is to say that they are designed so that they never request a resource with a lower number than the highest numbered resource that they hold 3. Circular wait is now impossible, because the chain of waiting cannot wrap around from the greatest back to the beginning. It will eventually unfold. Dining philosophers - Serialized Trace •P0 grabs Chopstick0, blocking P4 P1 •P1 grabs Chopstick1, blocking P0 P0 •P2 grabs Chopstick2, blocking P1 1 0 Rice 3 P4 •P3 grabs Chopstick3, blocking P2 2 4 P3 •P4 blocks trying to grab Chopstick0 P2 •P3 is free to grab Chopstick4 •P3 can eat! Dining Philosophers: Approach 3 Semaphore chopstick[5] = { 1, 1, 1, 1, 1 }; while (1) { if (i < ((i+1) % 5)) { P(chopstick[i]); P(chopstick[(i+1) % 5]); } else { P(chopstick[(i+1) % 5]); P (chopstick[i]); } <<< eat >>> V(chopstick[i]); V(chopstick[(i +1) % 5]); <<< think >>> }