COS 226_02 - Acknowledgement COS 226 Chapter 2 Mutual...

Info iconThis preview shows page 1. Sign up to view the full content.

View Full Document Right Arrow Icon
This is the end of the preview. Sign up to access the rest of the document.

Unformatted text preview: Acknowledgement COS 226 Chapter 2 Mutual Exclusion Some of the slides are taken from the companion slides for “The Art of Multiprocessor Programming” by Maurice Herlihy & Nir Shavit Why is Concurrent Programming so Hard? Try preparing a seven-course banquet By yourself With one friend With twenty-seven friends … Events An event a0 of thread A is Instantaneous No simultaneous events (break ties) a0 time Before we can talk about programs Need a language Describing time and concurrency Threads A thread A is (formally) a sequence a0, a1, ... of events “Trace” model Notation: a0 a1 indicates order a0 time Concurrency Thread A time a1 a2 … Thread B time 1 Interleavings Events of two or more threads Interleaved Not necessarily independent Intervals An interval A0 =(a0,a1) is Time between events a0 and a1 a0 time time A0 a1 Intervals may Overlap Intervals may be Disjoint b0 a0 time B0 a1 b1 a0 time b0 A0 a1 B0 b1 A0 Precedence Interval A0 precedes interval B0 Precedence b0 a0 time B0 b1 A0 a1 Notation: A0 Formally, B0 End event of A0 before start event of B0 Also called “happens before” or “precedes” 2 Precedence Ordering Precedence Ordering Remark: A0 B0 is just like saying 1066 AD 1492 AD, Middle Ages Renaissance, Never true that A A If A B then not true that B A If A B & B C then A C Funny thing: A B & B A might both be false! Repeated Events while (mumble) { a0; a1; } Implementing a Counter public class Counter { private long value; getAndIncrement() public long getAndIncrement() { value++; } } k-th occurrence of event a0 a0k k-th occurrence of A0k interval A0 =(a0,a1) Implementing a Counter public class Counter { private long value; getAndIncrement() public long getAndIncrement() { temp = value; value = temp + 1; return temp; } } Critical Section Block of code that can be executed by only one thread at a time Mutual Exclusion Standard way to approach mutual exclusion is through locks 3 Locks (Mutual Exclusion) Lock public interface Lock { lock(); public void lock(); unlock(); public void unlock(); } Locks (Mutual Exclusion) public interface Lock { lock(); public void lock(); public void unlock(); } acquire lock Locks (Mutual Exclusion) public interface Lock { lock(); public void lock(); public void unlock(); unlock(); } Using Locks public class Counter { private long value; lock; private Lock lock; getAndIncrement() public long getAndIncrement() { lock.lock(); lock.lock(); try { int temp = value; value = value + 1; } finally { lock.unlock(); lock.unlock(); } return temp; }} acquire lock release lock Using Locks public class Counter { private long value; lock; private Lock lock; getAndIncrement() public long getAndIncrement() { lock.lock(); lock.lock(); acquire try { int temp = value; value = value + 1; } finally { lock.unlock(); lock.unlock(); } return temp; }} Using Locks public class Counter { private long value; lock; private Lock lock; getAndIncrement() public long getAndIncrement() { lock.lock(); lock.lock(); try { int temp = value; value = value + 1; finally } finally { Release lock lock.unlock(); lock.unlock(); } (no matter what) return temp; }} Lock 4 Using Locks public class Counter { private long value; lock; private Lock lock; getAndIncrement() public long getAndIncrement() { lock.lock(); lock.lock(); try { int temp = value; value = value + 1; } finally { lock.unlock(); lock.unlock(); } return temp; }} Deadlock-Free If some thread calls lock() And never acquires lock Then other threads must complete lock() and unlock() calls infinitely often Critical section System as a whole makes progress Even if individuals starve At least one thread is completing infinitely often Starvation-Free If some thread calls lock() It will eventually return Two-Thread Conventions class … implements Lock { … thread// thread-local index, 0 or 1 public void lock() { ThreadID.get(); int i = ThreadID.get(); int j = 1 - i; … } } Individual threads make progress Two-Thread Conventions class … implements Lock { … thread// thread-local index, 0 or 1 public void lock() { ThreadID.get(); int i = ThreadID.get(); int j = 1 - i; … } } LockOne class LockOne implements Lock { private boolean flag = new boolean[2]; boolean public void lock() { flag[i] flag[i] = true; (flag[j flag[j]) while (flag[j]) {} } Henceforth: i is current thread, j is other thread 5 LockOne class LockOne implements Lock { private boolean flag = boolean new boolean[2]; public void lock() { flag[i] true flag[i] = true; (flag[j flag[j]) while (flag[j]) {} Set my flag } LockOne class LockOne implements Lock { private boolean flag = boolean new boolean[2]; public void lock() { flag[i] true flag[i] = true; flag[j]) while (flag[j]) {} Set my flag } Wait for other flag to go false Deadlock Freedom? Concurrent execution: flag[i] flag[i] = true; while (flag[j]){} (flag[j]){} flag[j flag[j] flag[j] = true; (flag[i flag[i]){} while (flag[i]){} LockTwo public class LockTwo implements Lock { private int victim; public void lock() { victim = i; while (victim == i) {}; } public void unlock() {} } If each thread sets its flag to true and waits for the other, they will wait forever No deadlock freedom Okay for sequential execution LockTwo public class LockTwo implements Lock { private int victim; Let other public void lock() { first victim = i; while (victim == i) {}; } public void unlock() {} } LockTwo go public class LockTwo implements Lock { Wait for private int victim; public void lock() { permission victim = i; while (victim == i) {}; } public void unlock() {} } 6 LockTwo public class Lock2 implements Lock { private int victim; public void lock() { Nothing victim = i; while (victim == i) {}; } public void unlock() {} } LockTwo Claims to do Satisfies mutual exclusion If thread i in CS LockTwo() public void LockTwo() { Then victim == j victim victim = i; while (victim == i) {}; Cannot be both 0 and 1 } Not deadlock free Sequential execution deadlocks Concurrent execution does not Deadlock freedom? Concurrent execution does not deadlock Sequential freedom deadlocks LockOne and LockTwo complement each other Peterson Lock Combine LockOne and LockTwo Peterson’s Algorithm public void lock() { flag[i] flag[i] = true; victim = i; (flag[j flag[j] while (flag[j] && victim == i) {}; } public void unlock() { flag[i] flag[i] = false; } Peterson’s Algorithm Announce I’m interested public void lock() { flag[i] flag[i] = true; victim = i; (flag[j flag[j] while (flag[j] && victim == i) {}; } public void unlock() { flag[i] flag[i] = false; } 7 Peterson’s Algorithm public void lock() { flag[i] flag[i] = true; Defer to other victim = i; while (flag[j] && victim == i) {}; (flag[j] flag[j } public void unlock() { flag[i] flag[i] = false; } Peterson’s Algorithm Announce I’m interested public void lock() { flag[i] flag[i] = true; Defer to other victim = i; while (flag[j] && victim == i) {}; (flag[j] flag[j } public void unlock() { Wait while other flag[i] flag[i] = false; interested & I’m } Announce I’m interested the victim Peterson’s Algorithm public void lock() { flag[i] flag[i] = true; Defer to other victim = i; (flag[j flag[j] while (flag[j] && victim == i) {}; } public void unlock() { Wait while other flag[i] flag[i] = false; interested & I’m } Mutual Exclusion Announce I’m interested public void lock() { flag[i] flag[i] = true; victim = i; (flag[j flag[j] while (flag[j] && victim == i) {}; • If thread 0 in critical section, – flag[0] = true, – victim = 1 If thread 1 in critical section, flag[1] flag[1] = true, victim victim = 0 No longer interested the victim Cannot both be true Deadlock Free public void lock() { … (flag[j flag[j] while (flag[j] && victim == i) {}; Starvation Free Thread i blocked only if j repeatedly re-enters so that flag[j] flag[j] == true victim == i public void lock() { flag[i] flag[i] = true; victim = i; (flag[j flag[j] while (flag[j] && victim == i) {}; } public void unlock() { flag[i] flag[i] = false; } Thread blocked only at while loop while Only if other’s flag is true only if it is the victim and Solo: others flag is false Both: one or the other must not be the victim When j re-enters it sets victim to j. victim So i gets in 8 The Filter Algorithm for n Threads There are n-1 “waiting rooms” called levels ncs At each level At least one enters level At least one blocked if many try Filter Filter class Filter implements Lock { int level; level[i] int level; // level[i] for thread i int victim; victim[L] int victim; // victim[L] for level L public Filter(int n) { new int[n]; level = new int[n]; level new int[n]; victim = new int[n]; for for (int i = 1; i < n; i++) { level[i] level[i] = 0; }} … cs } Only one thread makes it through Filter class Filter implements Lock { … public void lock(){ (int for (int L = 1; L < n; L++) { level[i] level[i] = L; victim[L] victim[L] = i; level[k] while ((∃ k != i level[k] >= L) && victim[L] victim[L] == i ); }} public void unlock() { level[i] level[i] = 0; }} Filter class Filter implements Lock { … public void lock() { (int for (int L = 1; L < n; L++) { level[i] level[i] = L; victim[L] victim[L] = i; level[k] while ((∃ k != i) level[k] >= L) && victim[L] victim[L] == i); }} public void release(int i) { level[i] level[i] = 0; }} One level at a time Filter class Filter implements Lock { … public void lock() { (int for (int L = 1; L < n; L++) { level[i] level[i] = L; victim[L] victim[L] = i; level[k] while ((∃ k != i) level[k] >= L) && victim[L] victim[L] == i); // busy wait }} Announce public void release(int i) { intention to level[i] level[i] = 0; }} enter level L Filter class Filter implements Lock { level[n]; int level[n]; victim[n]; int victim[n]; public void lock() { (int for (int L = 1; L < n; L++) { level[i] level[i] = L; victim[L] victim[L] = i; level[k] while ((∃ k != i) level[k] >= L) && victim[L] victim[L] == i); }} public void release(int i) { Give priority to level[i] level[i] = 0; anyone but me }} 9 Filter class Filter implements Lock { level[n]; int level[n]; victim[n]; int victim[n]; public void lock() { (int for (int L = 1; L < n; L++) { level[i] level[i] = L; victim[L] victim[L] = i; Wait as long as someone else is at same or higher level, and I’m designated victim Filter class Filter implements Lock { level[n]; int level[n]; victim[n]; int victim[n]; public void lock() { (int for (int L = 1; L < n; L++) { level[i] level[i] = L; victim[L] victim[L] = i; level[k] while ((∃ k != i) level[k] >= L) && victim[L] victim[L] == i); }} public void release(int i) { level[i] level[i] = 0; }} level[k] while ((∃ k != i) level[k] >= L) && victim[L] victim[L] == i); }} public void release(int i) { level[i] level[i] = 0; }} Thread enters level L when it completes the loop No Starvation Filter Lock satisfies properties: Just like Peterson Alg at any level So no one starves Waiting Starvation freedom guarantees that every thread that calls lock() eventually enters the critical section It however makes no guarantee about how long that can take But what about fairness? Threads can be overtaken by others Waiting Ideally if A calls lock() before B, then A should enter critical section before B However this does not currently work since we cannot determine which thread called lock() first Locks should thus be further defined Bounded waiting We divide lock() into two parts: Doorway section Always finishes in limited (bounded) steps Wait-free property Waiting section May take unlimited steps 10 Fairness Should be first-some-first served A lock is first-come-first-served if, whenever thread A finishes its doorway before thread B starts its doorway, then A cannot be overtaken by B. Fairness Again Filter Lock satisfies properties: No one starves But very weak fairness Can be overtaken arbitrary # of times That’s pretty lame… Bakery Algorithm Provides First-Come-First-Served How? Take a “number” Wait until lower numbers have been served Bakery Algorithm class Bakery implements Lock { boolean boolean flag; Label label; (int public Bakery (int n) { boolean[n]; flag = new boolean[n]; Label[n]; label = new Label[n]; (int for (int i = 0; i < n; i++) { flag[i] label[i] flag[i] = false; label[i] = 0; } } … Each thread takes a number in the doorway and waits until no thread with an earlier number is trying to enter it Bakery Algorithm flag[A] is a boolean flag indicating whether A wants to enter the critical section label[A] is an integer that contains thread A’s “number” when entering the bakery Bakery Algorithm class Bakery … public void flag[i] flag[i] = label[i] label[i] = implements Lock { lock() { true; max(label[0], …,label[n-1])+1; ,label[n- flag[k] while (∃k flag[k] (label[k label[k] label[i]); && (label[k] < label[i]); } 11 Bakery Algorithm class Bakery … public void flag[i] flag[i] = label[i] label[i] = implements Lock { Bakery Algorithm Doorway class Bakery … public void flag[i] flag[i] = label[i] label[i] = implements Lock { I’m interested lock() { true; max(label[0], …,label[n-1])+1; ,label[n- lock() { true; max(label[0], …,label[n-1])+1; ,label[n- flag[k] while (∃k flag[k] (label[k label[k] label[i]); && (label[k] < label[i]); } flag[k] while (∃k flag[k] (label[k label[k] label[i]); && (label[k] < label[i]); } Bakery Algorithm class Bakery … public void flag[i] flag[i] = label[i] label[i] = implements Lock { Bakery Algorithm Take increasing label (read labels in some arbitrary order) class Bakery … public void flag[i] flag[i] = label[i] label[i] = implements Lock { lock() { true; max(label[0], …,label[n-1])+1; ,label[n- lock() { true; max(label[0], …,label[n-1])+1; ,label[n- Someone is interested flag[k] while (∃k flag[k] (label[k label[k] label[i]); && (label[k] < label[i]); } flag[k] while (∃k flag[k] (label[k label[k] label[i]); && (label[k] < label[i]); } Label is created as one greater than the maximum of the other thread’s labels Bakery Algorithm If two threads execute their doorways concurrently, they may read the same maximum number Threads thus have unique pairs consisting of number as well as thread ID Bakery Algorithm (label[i],i) << (label[j],j) If and only if label[i] < label[j] OR label[i] = label[j] and i < j 12 Bakery Algorithm class Bakery implements Lock { flag[n]; boolean flag[n]; Someone label[n]; int label[n]; Bakery Algorithm In other words: Thread A must wait if: Another thread is interested AND the other thread’s number is lower than thread A OR public void lock() { flag[i] flag[i] = true; label[i] ,label[nlabel[i] = max(label[0], …,label[n-1])+1; flag[k] while (∃k flag[k] (label[k],k label[k],k) label[i],i)); && (label[k],k) << label[i],i)); } is interested With lower (label,i) in lexicographic order Another thread is interested AND the two threads have the same number but the other’s threads ID is smaller than A Bakery Algorithm class Bakery implements Lock { … public void unlock() { flag[i] flag[i] = false; } } Bakery Algorithm class Bakery implements Lock { … public void unlock() { flag[i] flag[i] = false; } } No longer interested No Deadlock There is always one thread with earliest label Ties are impossible (why?) First-come-first-served If A calls lock() before B, then A’s number is smaller than B’s number So B is locked out while flag[A] is true 13 Mutual Exclusion A and B cannot be in the critical section together, since one of them should have either an earlier label or an earlier label pair Bounded timestamps Labels in the Bakery lock grow without bounds In a long-lived system we may have to worry about overflow If a thread’s label silently rolled over from a large number to zero, the first-come-firstserved property no longer holds Bounded timestamps In the Bakery lock labels act as timestamps: They establish an order among contending threads Bounded timestamps Timestamps need the ability to: Scan – read the other thread’s timestamps Label – assign itself a larger timestamp We need to ensure that if one thread takes a label after another, then that thread’s label should be larger Since the timestamp is labelled in the doorway section, the timestamping must be wait-free The Good News One can construct a Wait-free (no mutual exclusion) Concurrent Timestamping system That never overflows The Good News Ba d One can construct a Wait-free (no mutual exclusion) This part Concurrent Timestamping system That never overflows is hard 14 Instead … We construct a Sequential timestamping system Each thread perform scan-and-label completely one after the other Uses mutual exclusion Precedence Graphs 0 1 2 3 Timestamps form directed graph Edge x to y Means x is later timestamp We say x dominates y Unbounded Counter Precedence Graph 0 1 2 3 Timestamping = move tokens on graph Atomically read others’ tokens move mine Ignore tie-breaking for now 15 ...
View Full Document

Ask a homework question - tutors are online