Obstruction free synchronization
Download
1 / 30

Obstruction-free synchronization - PowerPoint PPT Presentation


  • 114 Views
  • Uploaded on

Obstruction-free synchronization. Double-Ended Queues as an example. Article by: Maurice Herlihy , Victor Luchangco , Mark Moir. Presentation : Or Peri. Today’s Agenda. Two obstruction-free, CAS-based implementations of Double-ended queues . Linear array

loader
I am the owner, or an agent authorized to act on behalf of the owner, of the copyrighted work described.
capcha
Download Presentation

PowerPoint Slideshow about ' Obstruction-free synchronization' - abeni


An Image/Link below is provided (as is) to download presentation

Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author.While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server.


- - - - - - - - - - - - - - - - - - - - - - - - - - E N D - - - - - - - - - - - - - - - - - - - - - - - - - -
Presentation Transcript
Obstruction free synchronization

Obstruction-free synchronization

Double-Ended Queues as an example

Article by: Maurice Herlihy,

Victor Luchangco,

Mark Moir

Presentation : Or Peri


Today s agenda
Today’s Agenda

  • Two obstruction-free, CAS-based implementations of Double-ended queues.

    • Linear array

    • Circular array


Why obstruction free
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.


What s wrong with locks
What’s wrong with Locks?

  • Deadlocks

  • Low liveness

  • Fault-handling

  • Scalability


Pros cons
Pros & cons

  • Obstruction-freedom ensures No thread can be blockedby 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 cons1
Pros & cons

  • 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.


Dequeues
DEqueues

  • Double-ended queue- generalize FIFO queues and LIFO stacks.

  • Allows push\pop operations in both ends.


Dequeues what for
DEqueues- what for?

  • Remember “Job Stealing”?

  • One application of DEqueues is as processors’ jobs queues.

  • Each processor pops tasks from it’s own Dequeue’shead.

Job

Job

Job

Job

Job

Job

Job


Dequeues what for1
DEqueues- what for?

  • Upon fork(), it pushes tasks to it’s DEqueue‘shead.

  • If a processor’s queue is empty, it can “steal” tasks from another processor’s DEqueue‘stail.

Job

Job

Job

Job

Job

Job

Job

Job


Implementation
Implementation

  • First we’ll see the simpler, linear, array-based DEqueue.

  • Second stage will extend the first one to “wrap around” itself.


Implementation intro
Implementation – Intro

  • Two special “null” values: LNand 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:

    • Parameter: left/right

    • Returns: an array index

  • When Oracle(right) is invoked, the returned index is the leftmost RN value in A.


Implementation intro1
Implementation – Intro

  • Each element i in A has:

    • A value: i.val

    • A version counter:i.ctr

  • Version numbers are updated at every CAS operation.

  • Linearization point: point of changing a value in A.


Implementation intro2
Implementation – Intro

  • The Idea:

    • rightpush(v) will change the leftmost RN to v.

    • rightpop() will change the rightmost data to RN (and return it)

    • rightpush(v) returns “full” if there’s a non-RN value at A[MAX]

    • rightpop() returns “empty” if there are neighboring RN,LN

  • Right/left push/pop are symmetric, so we only show one side.


Implementation right push
Implementation – right push

  • Rightpush(v){

  • While(true){

  • k := oracle(right);

  • prev := A[k-1];

  • cur := A[k];

  • if(prev.val != RN and cur.val = RN){

  • if(k = MAX+1) return “full”;

  • if( CAS(&A[k-1], prev, <prev.val,prev.ctr+1>) )

  • if( CAS(&A[k], cur, <v,cur.ctr+1>) )

  • return “ok”;

  • } //end “if”

  • } //end “while”

  • } //end func


Implementation right pop
Implementation – right pop

  • Rightpop(){

  • While(true){

  • k := oracle(right);

  • cur := A[k-1];

  • next := A[k];

  • if(cur.val != RN and next.val = RN){

  • if(cur.val = LN and A[k-1] = cur)

  • return “empty”;

  • if( CAS(&A[k], next, <RN, next.ctr+1>) )

  • if( CAS(&A[k-1], cur, <RN,cur.ctr+1>) )

  • returncur.val;

  • } //end “if”

  • } //end “while”

  • } //end func


Linearizability
Linearizability

  • Relies on three claims:

    • 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.

    • In a rightpop() operation, at the moment we “CAS” A[k-1].val from some v to RN, A[k].val contains RN.

    • 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.


Linearizability1
Linearizability

  • The third claim:

    • 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:

  • cur := A[k-1];

  • next := A[k];

  • if(cur.val!= RN and next.val = RN){

  • if(cur.val= LN and A[k-1] = cur)

  • return“empty”;

  • A[k-1] didn’t change version number from line 4 to 7

  • so did A[k] from line 5 to 6.


Linearizability2
Linearizability

  • The first two claims hold similarly:

    • Since CAS operations check version numbers, only if no one interfered with another push/pop, we can perform the operation

    • In rightpush(v) for example:

  • prev := A[k-1];

  • cur := A[k];

  • if(prev.val!= RN and cur.val = RN){

  • if(k = MAX+1) return “full”;

  • if( CAS(&A[k-1], prev, <prev.val,prev.ctr+1>) )

  • if( CAS(&A[k], cur, <v,cur.ctr+1>) )

  • 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


Linearizability3
Linearizability

  • Implementing the Oracle() function:

    • For linearizability, we only need oracle() to return an index at range.

    • 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


Extension to circular array
Extension to circular array

  • The Idea:

    • A[0] is “immediately to the right” of A[MAX+1].

    • All indices are calculated modulo MAX+2.

  • Two main differences:

    • To return “full” we must be sure there are exactly two null entries.

    • A rightpushoperation may encounter a LN value  we’ll convert them into RN values (using another null character: DN).


Circular array invariants
Circular array - Invariants

  • 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 implementation
Circular array - Implementation

  • We don’t invoke oracle(right) directly.

  • Instead, we have rightCheckOracle() which returns:

    • K  an array index

    • Left  A[k-1]’s last content

    • Right  A[k]’s last content

  • This guarantees:

    • right.val = RN

    • Left.val != RN


Rightcheckedoracle
rightCheckedOracle()

  • While(true){

  • k := oracle(right);

  • left := A[k-1];

  • right := A[k];

  • if(right.val = RN and left.val != RN)

  • returnk,left,right;

  • if( right.val = DN and !(left.val in {RN,DN}) )

  • if( CAS(&A[k-1], left, <left.val, left.ctr+1>) )

  • if( CAS(&A[k], right, <RN,cur.ctr+1>) )

  • return k,<left.val,left.ctr+1>,

    <RN,right.ctr+1>;

  • } //end “while”


The major change rightpush v
The major change – rightPush(v)

  • 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”.


Rightpush v
rightPush(v)

  • While(true){

  • k,prev,cur := rightCheckedOracle();

  • next := A[k+1];

  • if( next.val = RN ) //change RN to v

  • if( CAS(&A[k-1], prev, <prev.val,prev.ctr+1> ) )

  • if( CAS(&A[k], cur, <v,cur.ctr+1>) )

  • return “ok”;

  • if( next.val = LN ) //change LN to DN

  • if( CAS(&A[k], cur, <RN, cur.ctr+1>) )

  • if( CAS(&A[k+1], next, <DN,next.ctr+1>) )

  • if(next.val = DN)


Rightpush v1
rightPush(v)

  • if(next.val = DN){

  • nextnext:= A[k+2];

  • if( !(nextnext.val in {RN,LN,DN}) )

  • if(A[k-1] = prev)

  • if(A[k] = cur)

  • return “full”;

  • if( nextnext.val = LN) //DN to RN

  • if( CAS(&A[k+2], nextnext,

    <nextnext.val,nextnext.ctr+1>) )

  • CAS(&A[k+1], next, <RN,next.ctr+1>);

  • } //end “if”

  • }//end “while”


Rightpop
rightPop()

  • While(true){

  • k,cur,next := rightCheckedOracle();

  • if( cur.val in {LN,DN} and A[k-1] = cur )

  • return “empty”;

  • if( CAS(&A[k], next, <RN, next.ctr+1>) )

  • if( CAS(&A[k-1], cur, <RN,cur.ctr+1>) )

  • returncur.val;

  • }//end “while”


Linearizability4
Linearizability

  • 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.


To sum up
To Sum up

  • 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



ad