Upload
gregory-holbrook
View
213
Download
0
Embed Size (px)
Citation preview
Proving that non-blocking algorithms don't block
Alexey GotsmanUniversity of Cambridge
Joint work with Byron Cook, Matthew Parkinson, and Viktor Vafeiadis
Proving that non-blocking algorithms don't block
Automatically proving liveness properties of non-blocking concurrent algorithms
Stay awake: nice links between programming, logic, and automatic verification Pick a class of programs Pick an appropriate logic Observe that proofs are simple and follow the same pattern Infer proofs automatically
Best of both worlds: automatic tool + understanding of the algorithms
Coarse-grained locking
Top
Inefficient as only onethread operates on the
list at a time
42 13 1 4 NULL
Non-blocking concurrency
Multiple threads operating on the data structure at the same time
Typical programming idiom:
...
L: read from a part of the data structure
do some work on the results
try to change the data structure
if interference is detected go to L
...
Non-blocking concurrency
Stacks, queues, skip lists, hash tables, etc.
Used in practice: e.g., java.util.concurrent
Complicated and hard to get right
Formal verification: Safety properties [Yahav+ 2003, Calcagno+ 2007, Amit+ 2007, Manevich+ 2008, Vafeiadis 2009]
Termination
?
Non-blocking concurrency: Treiber's stack
Top
42 13 1 4 NULL6
42 13
12
void push(data_t v) { Node *t, *x; x = new Node(); x->val = v; do { t = Top; x->next = t; } while(!CAS(&Top,t,x));}
data_t pop() { Node *t, *x; do { t = Top; if (t == NULL) return EMPTY; x = t->next; } while(!CAS(&Top,t,x)); return t->val;}
struct Node { Node *next; data_t val;} *Top;
Treiber's non-blocking stack: termination
void push(data_t v) { Node *t, *x; x = new Node(); x->val = v; do { t = Top; x->next = t; } while(!CAS(&Top,t,x));}
data_t pop() { Node *t, *x; do { t = Top; if (t == NULL) return EMPTY; x = t->next; } while(!CAS(&Top,t,x)); return t->val;}
struct Node { Node *next; data_t val;} *Top;
push or pop may not terminate if other threads continually modify Top
However: some operation will always terminate
This talk: logic & tool for proving lock-freedom
lock-freedom
From lock-freedom to termination
Lock-freedom of Treiber's stack
Push or Id
Shared stateRely/guarantee + separation logic for safety [Vafeiadis-Parkinson 2007]
void push(data_t v) { Node *t, *x; x = new Node(); x->val = v; do { t = Top; x->next = t; } while(!CAS(&Top,t,x));}
data_t pop() { Node *t, *x; do { t = Top; if (t == NULL) return EMPTY; x = t->next; } while(!CAS(&Top,t,x)); return t->val;}
struct Node { Node *next; data_t val;} *Top;
Pop or Id
Lock-freedom of Treiber's stack
Push or Id
void push(data_t v) { Node *t, *x; x = new Node(); x->val = v; do { t = Top; x->next = t; } while(!CAS(&Top,t,x));}
data_t pop() { Node *t, *x; do { t = Top; if (t == NULL) return EMPTY; x = t->next; } while(!CAS(&Top,t,x)); return t->val;}
struct Node { Node *next; data_t val;} *Top;
Pop or Id
Lock-freedom of Treiber's stack
The do loops terminate if no-one else executes Push or Pop infinitely often
No-one executes Push or Pop infinitely oftenHence, push and pop terminate
Push or Id
data_t pop() { Node *t, *x; do { t = Top; if (t == NULL) return EMPTY; x = t->next; } while(!CAS(&Top,t,x)); return t->val;}
void push(data_t v) { Node *t, *x; x = new Node(); x->val = v; do { t = Top; x->next = t; } while(!CAS(&Top,t,x));}
struct Node { Node *next; data_t val;} *Top;
Pop or Id
Layered proof
“I execute only Push, Pop, or Id”
“I don’t execute Push or Pop infinitely often”
“I don’t execute Push or Pop infinitely often”
“I terminate” “I terminate”
“I execute only Push, Pop, or Id”
Layered proofFormalised in a logic for liveness and heaps
Guarantees of the form
Run the safety checker:
Iteratively eliminate actions:
Proof search strategy
Proof valid for an arbitrary number of threads
Case studies
Treiber's stack [Treiber 1986]
HSY stack [Hendler+ 2004]
Non-blocking queue [Michael, Scott 1996]
Linked list [Michael 2002]
RDCSS [Harris+ 2002]
Conclusion
Automatic method with proofs reflecting algorithm structure
Hope the general approach can be reused
Lock-based lock-free algorithms require more complex environment assumptions