proving linearizability lock freedom
play

Proving linearizability & lock-freedom Viktor Vafeiadis - PowerPoint PPT Presentation

Proving linearizability & lock-freedom Viktor Vafeiadis MPI-SWS Michael & Scott non-blocking queue head tail null X 1 2 3 CAS compare & swap CAS (address, expectedValue, newValue) { atomic { if ( *address ==


  1. Proving linearizability & lock-freedom Viktor Vafeiadis MPI-SWS

  2. Michael & Scott non-blocking queue head tail null X 1 2 3

  3. CAS – compare & swap CAS (address, expectedValue, newValue) { atomic { if ( *address == expectedValue ) { *address = newValue; return true; } else { return false; } } } -A single hardware instruction on x86

  4. Michael & Scott non-blocking queue head tail null X 1 2 3

  5. Michael & Scott non-blocking queue null head tail 4 null X 1 2 3

  6. Michael & Scott non-blocking queue null head tail 4 X 1 2 3

  7. Michael & Scott non-blocking queue null head tail 4 X 1 2 3

  8. Michael & Scott non-blocking queue null head tail 4 X 1 2 3

  9. Enqueue & dequeue Don’t read the code ! dequeue () { enqueue(v) { while (true) { m = new Node(); m → val = v; h = Q → head; m → next = null; t = Q → tail; while (true) { n = h → next; t = Q → tail; if (Q → tail ≠ t) continue ; n = tail → next; if (h == t) { if (Q → tail ≠ tail) continue ; if (n == null) if (n == null) { return EMPTY; if (CAS(&t → next,n,m)) CAS(&Q → tail,t,n); break; } else { } else { if (CAS(&Q → head,h,n)) CAS(&Q → tail,t,n); return n → val; } } } } CAS(&Q → tail,t,n); } }

  10. RGSep actions (pre-/postcondition pairs)  Summarize the shared state updates head tail head tail Enqueue null null A B A head tail head tail Dequeue A B A B head tail head tail Advance tail pointer A B A B

  11. The actions of enqueue & dequeue enqueue(v) { dequeue () { m = new Node(); while (true) { m → val = v; Local h = Q → head; m → next = null; updates t = Q → tail; while (true) { n = h → next; t = Q → tail; if (Q → tail ≠ t) continue ; n = tail → next; if (h == t) { if (Q → tail ≠ t) continue ; if (n == null) if (n == null) { return EMPTY; if (CAS(&t → next,n,m)) ENQUEUE CAS(&Q → tail,t,n); ADV. TAIL break; } else { } else { if (CAS(&Q → head,h,n)) DEQUEUE CAS(&Q → tail,t,n); ADV. TAIL return n → val; } } } } } CAS(&Q → tail,t,n); ADV. TAIL }

  12. Length (first attempt) length() { num = 0; curr = Q → head → next; while (curr ≠ null) { num++; curr = curr → next; } return num; } head tail null X 1 2 3 4

  13. Length (second attempt) length() { num = 0; while (true) { t = Q → tail; n = tail → next; Read Q → tail, and ensure if (n == null) break ; that Q → tail → next == null CAS(&Q → tail,t,n); } curr = Q → head → next; while (curr ≠ null) { num++; if (curr == t) break; curr = curr → next; } return num; }

  14. Length (third attempt) length() { num = 0; do { h = Q → head; while (true) { Get a snapshot of t = Q → tail; Q → head and Q → tail n = tail → next; and ensure that if (n == null) break ; Q → tail → next==null. CAS(&Q → tail,t,n); } } while (h ≠ Q → head); curr = h → next; while (curr ≠ null) { num++; if (curr == t) break; curr = curr → next; } return num; }

  15. Verification challenge Functional correctness (linearizability): Every method executes ‘atomically’ and obeys a high-level specification VMCAI ’09 CAV ’10 Liveness properties, e.g. lock-freedom: At all times, some outstanding method call is guaranteed to terminate. POPL ’09

  16. Linearizability Every method executes ‘atomically’ & obeys a functional correctness specification Shared variable: AQ enqueue(v) spec AQ := append(singleton(v), AQ); return 0; dequeue() spec if (AQ == empty) { return EMPTY; } else { r := hd(AQ); AQ := tl(AQ); return r; }

  17. Linearizability & forward simulation Linearizability: The implementation (of every method) is a refinement of an atomic specification. Standard proof technique: forward simulation Abstract (spec) S abs S’ abs Concrete (impl) S conc S’ conc

  18. Linearization points The implementation is a refinement of an atomic specification. abstract execution concrete execution linearization point (LP)

  19. Linearization point of enqueue enqueue(v) { m := new Node(); m → val := v; m → next := null; while (true) { t := Q → tail; n := tail → next; if (Q → tail ≠ tail) continue ; if (n == null) { Lin. Point if (CAS(&t → next,n,m)) break; (provided CAS succeeds) } else { CAS(&Q → tail,t,n); } } CAS(&Q → tail,t,n); }

  20. Proof search for the LP ?  For each execution path of each method, choose a candidate LP  Check whether it is a valid LP Does this work ?

  21. Proof search for the LP ?  For each execution path of each method, choose a candidate LP  Check whether it is a valid LP Does this work ? Not quite. 1. LPs can be conditional 2. LPs can be in the code of another thread

  22. LP of dequeue, when it returns EMPTY dequeue () { while (true) { h := Q → head; t := Q → tail; n := h → next; LP provided if (Q → tail ≠ t) this test fails, and continue ; the h==t test succeeds if (h == t) { the n==null test succeeds if (n == null) return EMPTY; CAS(&Q → tail,t,n); } else { if (CAS(&Q → head,h,n)) Condition: return n → val; } ¬prophecy(Q → tail ≠ t) } ∧ h == t } ∧ n == null

  23. Key observation  Method executions that logically modify the state have a simple LP.  Method executions that do not logically modify the state often have a complex LP. So: Treat these two cases differently.  Search for LPs of executions that logically modify the state;  Do a non-constructive proof for executions that do not logically modify the state.

  24. Basic LP validation Auxiliary variable: lres 1. At the entry to the function: lres := UNDEF; 2. At the candidate e fg ectful LPs: assert( lres ==UNDEF); lres := method_spec(); 3. At the return points, check res == lres concrete abstract result result

  25. Validating pure executions Auxiliary variable: can_return 1. At the entry to the function: ∀ i. can_return [i] := false; 2. At every point, add a pure ‘LP checker’: if (abs_method() has no side-e fg ects) can_return [abs_method()] := true; 3. At the return points, check can_return [res] == true concrete result

  26. For example... dequeue spec if (AQ == empty) { return EMPTY; } else { r := hd(AQ); AQ := tl(AQ); return r; } pure lin. checker if (AQ == empty) { can_return [EMPTY] := true; }

  27. Enhanced LP validation Auxiliary variables: lres , can_return 1. At the entry to the function: lres := UNDEF; ∀ i. can_return [i] := false; 2. At the candidate e fg ectful LPs: assert( lres= =UNDEF); lres := abs_method() 3. Add pure checkers at every program point: assign can_return [i] := true if method_spec returns i & has no side-e fg ects. 4. At the return points, check (res== lres ) ∨ ( lres ==UNDEF ∧ can_return [res])

  28. LPs in other threads add (e) { ... } remove (e) { ... } contains (e) { c := H; while (c → val < e) c := c → next; return (c → val == e); } null -INF 1 3 7 +INF contains(5) || add(5); remove(3); remove(5)

  29. LPs in other threads add (e) { ... } remove (e) { ... } contains (e) { c := H; while (c → val < e) c := c → next; return (c → val == e); } null -INF 1 3 7 +INF c contains(5) || add(5); remove(3); remove(5)

  30. LPs in other threads add (e) { ... } remove (e) { ... } contains (e) { c := H; while (c → val < e) c := c → next; return (c → val == e); } null -INF 1 3 7 +INF c contains(5) || add(5); remove(3); remove(5)

  31. LPs in other threads add (e) { ... } remove (e) { ... } contains (e) { c := H; while (c → val < e) c := c → next; return (c → val == e); } null -INF 1 3 7 +INF c contains(5) || add(5); remove(3); remove(5)

  32. LPs in other threads add (e) { ... } remove (e) { ... } contains (e) { c := H; while (c → val < e) c := c → next; return (c → val == e); } null -INF 1 3 5 7 +INF c contains(5) || add(5); remove(3); remove(5)

  33. LPs in other threads add (e) { ... } remove (e) { ... } contains (e) { c := H; while (c → val < e) c := c → next; return (c → val == e); } 3 null -INF 1 5 7 +INF c contains(5) || add(5); remove(3); remove(5)

  34. LPs in other threads add (e) { ... } remove (e) { ... } contains (e) { c := H; while (c → val < e) c := c → next; return (c → val == e); } 5 3 null -INF 1 7 +INF c contains(5) || add(5); remove(3); remove(5)

  35. LPs in other threads add (e) { ... } remove (e) { ... } contains (e) { c := H; while (c → val < e) c := c → next; return (c → val == e); } 5 3 null -INF 1 7 +INF c contains(5) || add(5); remove(3); remove(5)

  36. Enhanced LP validation Auxiliary variables: lres , can_return 1. At the entry to the function: lres := UNDEF; ∀ i. can_return [i] := false; 2. At the candidate e fg ectful LPs: assert( lres= =UNDEF); lres := abs_method() 3. Add pure checkers at every program point (incl. program points in other threads) 4. At the return points, check (res== lres ) ∨ ( lres ==UNDEF ∧ can_return [res])

  37. Implementation CAVE: Concurrent Algorithm VErifier - RGSep action inference [VMCAI 2010] - Shape-value abstract domain [VMCAI 2009] - Algorithm for proving linearizability [CAV 2010] http://www.mpi-sws.org/~viktor/cave

Recommend


More recommend