Presentation is loading. Please wait.

Presentation is loading. Please wait.

Obstruction-free synchronization Article by: Maurice Herlihy, Victor Luchangco, Mark Moir Double-Ended Queues as an example Presentation : Or Peri.

Similar presentations


Presentation on theme: "Obstruction-free synchronization Article by: Maurice Herlihy, Victor Luchangco, Mark Moir Double-Ended Queues as an example Presentation : Or Peri."— Presentation transcript:

1 Obstruction-free synchronization Article by: Maurice Herlihy, Victor Luchangco, Mark Moir Double-Ended Queues as an example Presentation : Or Peri

2 Today’s Agenda Two obstruction-free, CAS-based implementations of Double-ended queues. o Linear array o Circular array

3 Why Obstruction-Free? Avoid locks. Non-blocking data sharing between threads. Greater flexibility in design compared with Lock- freedom and wait-freedom implementations. In practice, should provide the benefits of wait-free and lock-free programming.

4 What’s wrong with Locks? Deadlocks Low liveness Fault-handling Scalability

5 Defenitions (a reminder) A synchronization technique is wait-free if it ensures that every thread will continue to progress in the face of arbitrary delay (or failure) of other threads. It is lock-free if it ensures that some thread always makes progress. It is Obstruction-free if it guarantees progress for any thread that eventually executes in isolation.

6 Obstruction-freedom ensures No thread can be blocked by delays or failures of other threads. Obstruction-free algorithms are simpler, and can be applied to complex structures. It does not guarantee progress when two (or more) conflicting threads execute concurrently. To improve progress one might add a contention reducing mechanism (“back-off reflex”). Pros & cons

7 lock-free and wait-free implementations use such mechanisms, but in a way that imposes a large overhead, even without contention. In scenarios with low contention, programming an Obstruction-free algorithm with some contention- manager, there’s the benefit from the simple and efficient design. Pros & cons

8 Double-ended queue- generalize FIFO queues and LIFO stacks. DEqueues Allows push\pop operations in both ends.

9 Remember “Job Stealing”? One application of DEqueues is as processors’ jobs queues. Each processor pops tasks from it’s own Dequeue’s head. DEqueues- what for? Job

10 Upon fork(), it pushes tasks to it’s DEqueue‘s head. If a processor’s queue is empty, it can “steal” tasks from another processor’s DEqueue‘s tail. DEqueues- what for? Job

11 First we’ll see the simpler, linear, array-based DEqueue. Second stage will extend the first one to “wrap around” itself. Implementation

12 Two special “null” values: LN and RN Array A[0,…,MAX+1] holds state. MAX is the queue’s maximal capacity. INVARIANT: the state will hold: LN + values * RN + An Oracle() function: o Parameter: left/right o Returns: an array index When Oracle(right) is invoked, the returned index is the leftmost RN value in A. Implementation – Intro

13 Each element i in A has: o A value: i.val o A version counter: i.ctr Version numbers are updated at every CAS operation. Linearization point: point of changing a value in A. Implementation – Intro

14 The Idea: o rightpush(v) will change the leftmost RN to v. o rightpop() will change the rightmost data to RN (and return it) o rightpush(v) returns “full” if there’s a non-RN value at A[MAX] o rightpop() returns “empty” if there are neighboring RN,LN Right/left push/pop are symmetric, so we only show one side. Implementation – Intro

15 1)Rightpush(v){ 2) While(true){ 3) k := oracle(right); 4) prev := A[k-1]; 5) cur := A[k]; 6) if(prev.val != RN and cur.val = RN){ 7) if(k = MAX+1) return “full”; 8) if( CAS(&A[k-1], prev, ) ) 9) if( CAS(&A[k], cur, ) ) 10) return “ok”; 11) } //end “if” 12) } //end “while” 13) } //end func Implementation – right push

16 1)Rightpop(){ 2) While(true){ 3) k := oracle(right); 4) cur := A[k-1]; 5) next := A[k]; 6) if(cur.val != RN and next.val = RN){ 7) if(cur.val = LN and A[k-1] = cur) 8) return “empty”; 9) if( CAS(&A[k], next, ) ) 10) if( CAS(&A[k-1], cur, ) ) 11) return cur.val; 12) } //end “if” 13) } //end “while” 14) } //end func Implementation – right pop

17 Relies on three claims: o In a rightpush(v) operation, at the moment we “CAS“ A[k].val from an RN value to v, A[k-1].val is not RN. o In a rightpop() operation, at the moment we “CAS” A[k-1].val from some v to RN, A[k].val contains RN. o If rightpop() returns “empty”, then at the moment it performed next:=A[k] (and just after: cur:=A[k-1]), these two values were LN and RN. Linearizability

18 The third claim: o If rightpop() returns “empty”, then at the moment it performed next:=A[k] (and just after: cur:=A[k-1]), these two values were LN and RN. holds since: 4)cur := A[k-1]; 5)next := A[k]; 6) if(cur.val != RN and next.val = RN){ 7) if(cur.val = LN and A[k-1] = cur) 8) return “empty”; A[k-1] didn’t change version number from line 4 to 7 so did A[k] from line 5 to 6. Linearizability

19 The first two claims hold similarly: o Since CAS operations check version numbers, only if no one interfered with another push/pop, we can perform the operation o In rightpush(v) for example: 4)prev := A[k-1]; 5)cur := A[k]; 6)if(prev.val != RN and cur.val = RN){ 7) if(k = MAX+1) return “full”; 8) if( CAS(&A[k-1], prev, ) ) 9) if( CAS(&A[k], cur, ) ) Counter didn’t change (upon success) from line 5 to 9, hence so did the value. Same holds for the neighbor (k-1) from line 4 to 8 Linearizability

20 Implementing the Oracle() function: o For linearizability, we only need oracle() to return an index at range. o For Obstruction-freedom we have to show that it is eventually accurate if invoked repeatedly without interference. Naïve approach is to simply go over the entire array and look for the first RN. Another approach is to keep “hints” (last position, for instance), and search around them. We can update these hints frequently or seldom with respect to cache locations… but that’s off-topic Linearizability

21 The Idea: o A[0] is “immediately to the right” of A[MAX+1]. o All indices are calculated modulo MAX+2. Two main differences: o To return “full” we must be sure there are exactly two null entries. o A rightpush operation may encounter a LN value  we’ll convert them into RN values (using another null character: DN). Extension to circular array

22 All null values are in a contiguous sequence in the array. This sequence is of the form: RN* DN* LN* There are at least 2 different types of null values in the sequence. Circular array - Invariants

23 We don’t invoke oracle(right) directly. Instead, we have rightCheckOracle() which returns: o K  an array index o Left  A[k-1]’s last content o Right  A[k]’s last content This guarantees: o right.val = RN o Left.val != RN Circular array - Implementation

24 rightCheckedOracle() 1)While(true){ 2) k := oracle(right); 3) left := A[k-1]; 4) right := A[k]; 5) if(right.val = RN and left.val != RN) 6) return k,left,right; 7) if( right.val = DN and !(left.val in {RN,DN}) ) 8) if( CAS(&A[k-1], left, ) ) 9) if( CAS(&A[k], right, ) ) 10) return k,, ; 11) } //end “while”

25 The array is not “full” when A[k+1] is RN. this is since A[k] is RN and an Invariant holds that “There are at least 2 different types of null values in the sequence”. So, if A[k+1] = LN  try converting it to DN If A[k+1] = DN  try converting it to RN In this case, we need to check “nextnext”. The major change – rightPush(v)

26 rightPush(v) 1)While(true){ 2) k,prev,cur := rightCheckedOracle(); 3) next := A[k+1]; 4) if( next.val = RN )//change RN to v 5) if( CAS(&A[k-1], prev, ) ) 6) if( CAS(&A[k], cur, ) ) 7) return “ok”; 8) if( next.val = LN ) //change LN to DN 9) if( CAS(&A[k], cur, ) ) 10) if( CAS(&A[k+1], next, ) ) 11) if(next.val = DN)

27 rightPush(v) 11) if(next.val = DN){ 12) nextnext:= A[k+2]; 13) if( !(nextnext.val in {RN,LN,DN}) ) 14) if(A[k-1] = prev) 15) if(A[k] = cur) 16) return “full”; 17) if( nextnext.val = LN) //DN to RN 18) if( CAS(&A[k+2], nextnext, ) ) 11) CAS(&A[k+1], next, ); 12) } //end “if” 13)}//end “while”

28 rightPop() 1)While(true){ 2) k,cur,next := rightCheckedOracle(); 3) if( cur.val in {LN,DN} and A[k-1] = cur ) 4) return “empty”; 5) if( CAS(&A[k], next, ) ) 6) if( CAS(&A[k-1], cur, ) ) 7) return cur.val; 8)}//end “while”

29 Is harder to prove in this case (there’s a whole other article just to do so). The main difficulty: proving that when rightPush(v) changes a value, it has an RN or an DN to it’s right. There are 5 lines in the code (of the right side functions) which may interrupt with this, but they are all using CAS, and intuitively, the.ctr values should assure correctness. Linearizability

30 We’ve seen Two Obstruction-free implementations of a Dequeue. As promised, they are pretty simple. Hopefully, I’ve managed to demonstrate the main degradation, as well as an intuition as to why it’s a good solution for relatively low contention scenarios To Sum up

31 Questions? ?


Download ppt "Obstruction-free synchronization Article by: Maurice Herlihy, Victor Luchangco, Mark Moir Double-Ended Queues as an example Presentation : Or Peri."

Similar presentations


Ads by Google