Distributed Algorithms 22903 Lockfree stack algorithms Lecturer Danny
Distributed Algorithms (22903) Lock-free stack algorithms Lecturer: Danny Hendler
Wait-freedom vs. lock-freedom • Wait-freedom – each process completes its operation in a finite number of its own steps • Lock-freedom – some process completes its operation after a finite number of steps is taken 2
Treiber’s stack algorithm Top val next … val next Push(int v, Stack S) 1. n : = new NODE ; create node for new stack item 2. n. val : = v ; write item value 3. do forever ; repeat until success 4. node top : = S. top 5. n. next : = top ; next points to current (LIFO order) 6. if compare&swap(S, top, n) ; try to add new item 7. return ; return if succeeded 8. od 3
Treiber’s stack algorithm (cont’d) Top val next … Pop(Stack S) 1. do forever 2. top : = S. top 3. if top = null 4. return empty 5. if compare&swap(S, top. next) 6. return-val=top. val 7. free top 8. return-val 9. od val next 4
Treiber’s stack algorithm (cont’d) It is easily seen that the alg is linearizable and lock-free A disadvantage of the algorithms is that… It has a sequential bottleneck. 5
An elimination backoff stack algorithm Key idea: pairs of push/pop operations may collide and eliminate each other without accessing a central stack. Central stack Top val next … val next collision array 6
Collision scenarios push pop push Collision array Central stack Top val next … val next 7
Data structures Each stack operation is represented by a Thread. Info structure struct Thread. Info { id ; the identifier of the thread performing the operation op ; a PUSH/POP opcode cell ; a cell structure spin ; duration to spin } Struct Cell { ; a representation of stack item as in Treiber pnext ; pointer to the next cell pdata ; stack item } p 1 Thread Info p 2 p 3 p 4 Thread Info Location array pn collision array p 1 p 7 8
Elimination-backoff stack code void Stack. Op(Thread. Info* p. Info) { 1. if (Try. Perform. Stack. OP(p) == FALSE) ; if operation not applied to central stack 2. Les. Op(p) ; try to eliminate operation by colliding with an opposite-type operation 3. return void Les. OP(Thread. Info * p) 1. while (1) 2. location[mypid]=p ; announce arrival 3. pos=Get. Position(p) ; get a random position at the collision array 4. him=collision[pos] ; read current value of that position 5. while (!compare&swap(&collision[pos], him, mypid); try to write own ID 6. him=collision[pos] ; continue till success 7. if (him != empty) ; if read an ID of another thread 8. q=location[him] ; read a pointer to the other thread’s info 9. if (q!=NULL && q->id=him && q->op != p->op) ; if may collide 10. if (compare&swap(&location[mypid], p, NULL) ; try to prevent unwanted collisions 11. if (Try. Collision(p, q)==true) ; if collided successfully 12. return ; return code is already at Thread. Info structure 13. else goto stack ; try to apply operation to central stack 14. else Finish. Collision(p), return ; extract information and finish 15. delay (p->spin) ; Wait for other thread to collide with me 16. if (!compare&swap(&location[mypid], p, NULL) ; if someone collided with me 17. Finish. Collision(p), return; Extract information and finish 9 stack: if (Try. Perform. Stack. Op(p)==TRUE) return ; try to apply operation to central stack
Elimination-backoff stack code (cont’d) void Try. Collision(Thread. Info* p, Thread. Info *q) 1. if (p->op==PUSH) 2. if (compare&swap(&location[him], q, p)) ; give my record to other thread 3. return TRUE 4. else 5. return FALSE 6. else 7. if (compare&swap(&location[him], q, NULL)) 8. p->cell=q->cell ; get pointer to PUSH operation’s cell 9. location[mypid]=NULL; 10. return TRUE 11. else 12. return FALSE void Finish. Collision(Thread. Info* p) 1. if (p->op==POP) 2. p->pcell=location[mypid]->pcell 3. location[mypid]=NULL 10
Elimination-backoff stack code (cont’d) Why is this implementation linearizable? Can a record be recycled once popped from stack? 11
Recycling: Simple Solution • Each thread has a free list of unused queue nodes • Allocate node: pop from list • Free node: push onto list • Use CAS for atomicity • Deal with underflow somehow … © Herlihy-Shavit 2007 12
Why Recycling is Hard head tail Want to rediret head from grey zzz… to red Free pool © Herlihy-Shavit 2007 13
Why Recycling is Hard head tail zzz Free pool © Herlihy-Shavit 2007 14
Why Recycling is Hard head tail Yawn! Free pool © Herlihy-Shavit 2007 15
Why Recycling is Hard head tail CAS OK, here I go! Free pool © Herlihy-Shavit 2007 16
Final State head tail What went wrong? Free pool © Herlihy-Shavit 2007 17
The Dreaded ABA Problem head tail Head pointer has value A Threads value A © Herlihy-Shavit 2007 18
Dreaded ABA continued head zzz © Herlihy-Shavit 2007 tail Head pointer has value B Node A freed 19
Dreaded ABA continued head Yawn! © Herlihy-Shavit 2007 tail Head pointer has value A again Node A recycled & reinitialized 20
Dreaded ABA continued head tail CAS succeeds because pointer matches even though pointer’s meaning has changed © Herlihy-Shavit 2007 21
The Dreaded ABA Problem • Is a result of CAS() semantics (Sun, Intel, AMD) • Does not arise with Load. Locked/Store-Conditional (IBM) © Herlihy-Shavit 2007 22
Dreaded ABA – A Solution • • Tag each pointer with a counter Unique over lifetime of node Pointer size vs word size issues Overflow? – Don’t worry be happy? – Bounded tags © Herlihy-Shavit 2007 23
- Slides: 23