1 / 62

Verifying Optimistic Concurrency: Prophecy Variables and Backwards Reasoning

Verifying Optimistic Concurrency: Prophecy Variables and Backwards Reasoning . Serdar Tasiran Koç University Istanbul, Turkey Tayfun Elmas Shaz Qadeer Ali Sezgin Koç University Microsoft Research Koç University Istanbul , Turkey Redmond, WA Istanbul, Turkey. The .1% Problem (but cute!).

danika
Download Presentation

Verifying Optimistic Concurrency: Prophecy Variables and Backwards Reasoning

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. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Verifying Optimistic Concurrency: Prophecy Variables and Backwards Reasoning SerdarTasiran Koç University Istanbul, Turkey TayfunElmasShaz Qadeer Ali SezginKoç UniversityMicrosoft ResearchKoç University Istanbul, Turkey Redmond, WAIstanbul, Turkey

  2. The .1% Problem(but cute!) SerdarTasiran Koç University Istanbul, Turkey TayfunElmasShaz Qadeer Ali SezginKoç UniversityMicrosoft ResearchKoç University Istanbul, Turkey Redmond, WAIstanbul, Turkey

  3. The Gist • Proofs of linearizability and refinement for concurrent data structures • Verifying the critical 1% • Experience: • Lipton-style reduction and abstraction really help. • History variables are often needed • For data structures using optimistic concurrency (STM’s, etc.) proofs need to refer to the future • Prophecy variables • This talk • QED + prophecy variables • Thinking backwards in time

  4. Static Reduction Proofs for Optimistic Concurrency • Goal: Verify programs that use optimistic concurrency • “A Compositional Method for Verifying Software Transactional Memory Implementations” • Partly mechanically checked • Reduction  Coarser atomic blocks • Optimistic concurrency:Proceed assuming non-interference • Abort, undo and/or retry if interference detected • Approach: Static proof system QED [POPL ‘09, PADTAD ‘09] • Challenge in applying QED to optimistic concurrency: • Same code, different reduction proofs for different futures • Need mechanism for referring to execution’s future • Prophecy variables and backwards reasoning in QED

  5. Outline • Forward reasoning overview • Lock-protected increment • QED proof • History variables, “assert” annotations • Backward reasoning: Temporal dual • Optimistic concurrency example: Copy • Why forward reasoning doesn’t work • Temporally dual approach • Prophecy variables, “tressa” annotations • Generalized (back and forth) QED • Examples

  6. History Variable Annotations Make Static Mover Check Pass Thread 1 acquire (lock); a := tid1; assert a == tid1; t1 = x; t1 = t1 + 1 assert a == tid1; x = t1; assert a == tid1;release(lock); a := 0; Thread 2 acquire (lock); a := tid2; assert a == tid2; t2 = x; t2 = t2 + 1 assert a == tid2; x = t2; assert a == tid2;release(lock); a := 0; R B B B L • assert a == tid1; x = t1; andassert a == tid2; x = t2; commute • αβ≤ βα • Because αβ and βα result in assertion violations.

  7. Borrowing and paying back assertions Invariant: (lock == true) (a != 0) Discharges the assertions inc (): int t; acquire (lock); a := tid; assert a == tid; t = x; t = t + 1 assert a == tid; x = t; assert a == tid;release(lock); a := 0; inc (): int t; acquire (lock); a := tid; assert a == tid;t = x; t = t + 1 assert a == tid;x = t; assert a == tid;release(lock); a := 0; R B B B L REDUCE & RELAX

  8. Outline • Forward reasoning overview • Lock-protected increment • QED proof • History variables, “assert” annotations • Backward reasoning: Temporal dual • Optimistic concurrency example: Copy • Why forward reasoning doesn’t work • Temporally dual approach • Prophecy variables, “tressa” annotations • Generalized (back and forth) QED • Examples

  9. Optimistic Concurrency Example Copy(fr: Obj, to: Obj){ atomic{ version := fr.ver; value := fr.val;} atomic{ if (version == fr.ver) {to.val := value; to.ver := to.ver + 1;} } } Wrt(to: Obj, newVal: int){ atomic{ to.val := newVal; to.ver := to.ver + 1;} }

  10. Optimistic Concurrency Example Copy(fr: Obj, to: Obj){ action SS(fr): atomic{ version := fr.ver; value := fr.val;} action ConfNWrt(fr, to): atomic{ if (version == fr.ver) {to.val := value; to.ver := to.ver + 1;} } } Wrt(to: Obj, newVal: int){ atomic{ to.val := newVal; to.ver := to.ver + 1;} }

  11. Optimistic Concurrency Example Copy(fr: Obj, to: Obj){ action SS(fr): atomic{ version := fr.ver; value := fr.val;} action ConfNWrt(fr, to): atomic{ if (version == fr.ver) {to.val := value; to.ver := to.ver + 1;} } } Wrt(to: Obj, newVal: int){ atomic{ to.val := newVal; to.ver := to.ver + 1;} } Goal: Prove that SS(fr) is a right mover

  12. Reduction for Optimistic Concurrency • Copy(x, to_y) running concurrently withWrt(x) • Does SS always commute to the right of Wrt? • Failing Copy (Wrt interferes with Copy) T1: SS(x) ConfNWrt(x, to_y) T2:Wrt(x, val) • Succeeding Copy (No interference) T1: SS(x) ConfNWrt(x, to_y) T2: Wrt(x, val)

  13. Reduction Argument for Optimistic Concurrency • Failing Copy T1: SS(x) ConfNWrt(x, to_y) T2:Wrt(x, val) • Snapshot taken but ConfNWrtdoes not write to to_y. • Abstract SS: action SS(fr): atomic{ version := fr.ver; value := fr.val;} action SS_Abs(fr): atomic{ havoc version, value; }

  14. Copy Example After Abstraction Copy(fr: Obj, to: Obj){ action SS_Abs(fr): atomic{ havoc version, value; } action ConfNWrt(fr, to): atomic{ if (version == fr.ver) {to.val := value; to.ver := to.ver + 1;} } } Wrt(to: Obj, newVal: int){ atomic{ to.val := newVal; to.ver := to.ver + 1;} }

  15. Problem: Too much abstraction • Succeeding Copy (No interference) T1: SS_Abs(x) ConfNWrt(x, to_y) T2: Wrt(x, val) • Arbitrary value written to to_y! • Want to abstract SS(x) toSS_Abs(x) only in executionsin which interference occurs (later). • But, SS_Abs doesn’t yet know whether interference will occur. • Need mechanism to refer to the future of the execution.

  16. Prophecy Variables • Prophecy variable: Auxiliary variable, encodes future non-determinism • Allows actions to refer to future locally • Can use in annotations, abstraction. • Different reduction proofs for different futures • Concurrent systems: Non-determinism due to thread interleaving p = R, G or B

  17. Introducing a Prophecy Variable action ConfNWrt(fr, to): atomic{ if (version == fr.ver) {to.val := value; to.ver := to.ver + 1;} } action ConfNWrt(fr, to): atomic{ if (version == fr.ver) { p =: true; to.val := value; to.ver := to.ver + 1; } else { p =: false; } } }

  18. Backwards Assignment “=:” • Backwards assignment p =: x; • Shorthand for atomic{ assume p == x; havoc p;} • p should match x • p’ can have any value • Thinking backwards in time: Assigns the value of x to p • In the execution, up to the point the following action is taken if (version == fr.ver) { p =: true; to.val := value; to.ver := to.ver + 1; } else { p =: false;} p is true iff the version number check (later) succeeds.

  19. Prophecy Variable Introduction: Soundness • Annotating actionα(s,s’) with prophecy variable p α(s,s’) becomesβ(s,p, s’,p’) • Must satisfy • p’. p. β(s,p, s’,p’) (History variables: h. h’. β(s,h, s’,h’) ) • Backwards assignment satisfies this • Soundness: • Every state of every execution can be annotated with a value of p. τ

  20. Abstraction Constrained by Prophecy Variable action SS_P(x): atomic{ if (p) {version := x.ver; value := x.val;} else { havoc version, value;} }

  21. Reduction Proof of Optimistic Concurrency • Succeeding Copy T1: SS(x) ConfNWrt(x, to_y) T2: Wrt(x, val) • SS(x) commutes to the right of Wrt(x, val) • Copy succeeds Wrt(x, val) never immediately follows SS(x) • Need to annotate SS(x) with this fact. • Similar case: assert a == tid1; x = t1; andassert a == tid2; x = t2; • These two actions cannot follow each other

  22. Reduction Proof of Optimistic Concurrency • Copy succeeds Wrt(x, val) never immediately follows SS(x) • Need to annotate SS(x) with this fact. • But SS(x) doesn’t yet know whether this is a succeeding Copy. • Think backwards: • Walk back from the end of an execution, • When we reach s2, we know whether Copy has been successful. • Restated fact: If Copy succeeded, at s2, the version number snapshot should be up-to-date. SS(x) ConfNWrt(x) … … … s0 s1 s2

  23. Putting it all together Copy(fr: Obj, to: Obj){ action SS_P(fr): atomic{ if (p) {version := fr.ver; value := fr.val;} else { havoc version, value;} tressa p ==> (version >= fr.ver); } action ConfNWrt(fr, to): atomic{ if (version == fr.ver) { p =: true; to.val := value; to.ver := to.ver + 1; } else { p =: false; } } }

  24. tressa : Temporal Dual of assert • tressa: Mechanism to annotate actions with assertions that can refer to prophecy variables • assert: Discharged by reasoning about history of execution. • tressa: Temporal dual of assert • Example:y := y+1; z := z-1; assume (x == 0);

  25. tressa : Temporal Dual of assert • Example:y := y+1; // x == 0 or execution blocksz := z-1; // x == 0 or execution blocks assume (x == 0); • But • atomic{ assert x == 0; y := y+1;} atomic{ assert x == 0; z := z-1;} assume (x == 0);does not work! • Cannot discharge the assertions!

  26. tressa : Temporal Dual of assert • Example:y := y+1; // x == 0 or execution blocksz := z-1; // x == 0 or execution blocks assume (x == 0); • tressa φ: Either φ holds in the post state, or execution does not terminate (blocks). • atomic{ y := y+1; tressa x == 0;} atomic{ z := z-1; tressa x == 0;} assume (x == 0); • tressa annotations discharged by backwards reasoning within an atomic block. • Discharged tressaφ: You cannot come back from a final state of the program and violate φ

  27. Discharging tressa’s inc (): int t; acquire (lock); p =: 0 tressa a == tid; t = x; t = t + 1 tressa a == tid; x = t; release(lock); p =: tid; inc (): int t; acquire (lock); p =: 0; tressa p == tid;t = x; t = t + 1 tressa a == tid;x = t; release(lock); p =: tid; R B B B L REDUCE & RELAX

  28. Actions with assert’s and tressa’s • Canonical action α: {assert a1; τ1; tressa p1} • τ1( s, s’) : Transition predicate • a1(s): assert predicate on the pre-state • p1(s’): tressa predicate on the post-state • Operational semantics: • assert and tressa predicates have no effect on execution • Execution has assert violation if any αi’sassert predicate is violated. • Execution has tressa violation if any αi’stressa predicate is violated. • QED± preserves assert and tressa violations α0 α1 α2 αn-1 … s0 s1 s2 s3 sn-1 sn

  29. Abstraction and Mover Checks with tressa’s abstracts {assert a1; τ1; tressa p1} ≤ {assert a2; τ2; tressa p2} • Preserve assert violations: a2 a1 • Preserve assert violations: p2 p1 • Forward simulate or replace with assert violation: τ1 (s,s’) τ2 (s,s’) ∨ a2(s) • Backward simulate orreplace with tressa violation: τ1 (s,s’) τ2 (s,s’) ∨ p2(s’) • Does α commute to the right of β ? αβ≤βα

  30. Putting it all together Copy(fr: Obj, to: Obj){ action SS_P(fr): atomic{ if (p) {version := fr.ver; value := fr.val;} else { havoc version, value;} tressa p ==> (version >= fr.ver); } action ConfNWrt(fr, to): atomic{ if (version == fr.ver) { p =: true; to.val := value; to.ver := to.ver + 1; } else { p =: false; } } }

  31. SS_P Wrt ≤ Wrt SS_P (Case 1) p == false Wrt(x) SS_P (x): havoc version, value action SS_P(x): atomic{ if (p) {version :=x.ver; value :=x.val;} else { havoc version, value;} tressa p ==> (version >= ver); } Wrt(x) { atomic{ x.val := newVal; x.ver := x.ver + 1;} }

  32. SS_P Wrt ≤ Wrt SS_P (Case 1) p == false SS_P(x): havoc version, value Wrt(x) Wrt(x) SS_P (x): havoc version, value action SS_P(x): atomic{ if (p) {version :=x.ver; value :=x.val;} else { havoc version, value;} tressa p ==> (version >= ver); } Wrt(x) { atomic{ x.val := newVal; x.ver := x.ver + 1;} }

  33. SS_P Wrt ≤ Wrt SS_P (Case 2) p == true Wrt(x) SS_Proph(x): tressa(version>=x.ver) action SS_P(x): atomic{ if (p) {version :=x.ver; value :=x.val;} else { havoc version, value;} tressa p ==> (version >= ver); } Wrt(x) { atomic{ x.val := newVal; x.ver := x.ver + 1;} }

  34. SS_P Wrt ≤ Wrt SS_P (Case 2) p == true Wrt(x) SS_Proph(x): tressa(version>=x.ver) version < x.ver(Case 2) action SS_P(x): atomic{ if (p) {version :=x.ver; value :=x.val;} else { havoc version, value;} tressa p ==> (version >= ver); } Wrt(x) { atomic{ x.val := newVal; x.ver := x.ver + 1;} }

  35. SS_P Wrt ≤ Wrt SS_P (Case 2) p == true version < x.ver Wrt(x) SS_Proph(x): tressa(version>=x.ver) version < x.ver(Case 2) action SS_P(x): atomic{ if (p) {version :=x.ver; value :=x.val;} else { havoc version, value;} tressa p ==> (version >= ver); } Wrt(x) { atomic{ x.val := newVal; x.ver := x.ver + 1;} }

  36. SS_P Wrt ≤ Wrt SS_P (Case 2) p == true SS_P (x): tressa(version>=x.ver) version < x.ver Wrt(x) SS_Proph(x): tressa(version>=x.ver) version < x.ver(Case 2) action SS_P(x): atomic{ if (p) {version :=x.ver; value :=x.val;} else { havoc version, value;} tressa p ==> (version >= ver); } Wrt(x) { atomic{ x.val := newVal; x.ver := x.ver + 1;} }

  37. SS_P Wrt ≤ Wrt SS_P (Case 3) p == true ✔ Wrt(x) SS_Proph(x): tressa(version>=x.ver) action SS_P(x): atomic{ if (p) {version :=x.ver; value :=x.val;} else { havoc version, value;} tressa p ==> (version >= ver); } Wrt(x) { atomic{ x.val := newVal; x.ver := x.ver + 1;} }

  38. SS_P Wrt ≤ Wrt SS_P (Case 3) p == true ✔ Wrt(x) SS_Proph(x): tressa(version>=x.ver) version == x.ver action SS_P(x): atomic{ if (p) {version :=x.ver; value :=x.val;} else { havoc version, value;} tressa p ==> (version >= ver); } Wrt(x) { atomic{ x.val := newVal; x.ver := x.ver + 1;} }

  39. SS_P Wrt ≤ Wrt SS_P (Case 3) p == true ✔ Wrt(x) SS_Proph(x): tressa(version>=x.ver) version+1 == x.ver version == x.ver action SS_P(x): atomic{ if (p) {version :=x.ver; value :=x.val;} else { havoc version, value;} tressa p ==> (version >= ver); } Wrt(x) { atomic{ x.val := newVal; x.ver := x.ver + 1;} }

  40. SS_P Wrt ≤ Wrt SS_P (Case 3) p == true SS_Proph(x): tressa(version>=x.ver) Wrt(x) SS_Proph(x): tressa(version>=x.ver) version+1 == x.ver version == x.ver action SS_P(x): atomic{ if (p) {version :=x.ver; value :=x.val;} else { havoc version, value;} tressa p ==> (version >= ver); } Wrt(x) { atomic{ x.val := newVal; x.ver := x.ver + 1;} }

  41. Copy is Atomic R Copy(fr: Obj, to: Obj){ action SS_P(fr): atomic{ if (p) {version := fr.ver; value := fr.val;} else { havoc version, value;} tressa p ==> (version >= ver); } action ConfNWrt(fr, to): atomic{ if (version == fr.ver) { p =: true; to.val := value; to.ver := to.ver + 1; } else { p =: false; } } }

  42. Proving Optimistic Concurrency in QED • tressa’s allow one to express the following fact locally • If action b immediately follows action a, then this execution will eventually block • Optimistic concurrency proof template: • Introduce prophecy variables • Backwards assign them when non-determinism is resolved • Annotate earlier actions with tressa’s • Perform a case split in the proof based on possible futures • When blocks are large enough, discharge tressa’s by reasoning backwards in time.

  43. Example 2: ReadPair procedure ReadPair(a: int, b: int) returns (s: bool, da: Obj, db: Obj) { varva: int, vb: int; 1: atomic { va := m[a].v; da := m[a].d; } 2: atomic { vb := m[b].v; db := m[b].d; } 3: s := true; 4: atomic { if (va < m[a].v) { s:= false; } } 5: atomic { if (vb < m[b].v) { s:= false; } } 6: if (!s) { da := nil; db := nil; } } procedure Write(a: int, d: Obj) { atomic { m[a].d := d; m[a].v := m[a].v+1; } }

  44. Example 3: Multiset (Insert/Lookup) procedure Lookup(x: data) returns result: bool; { f := false; i := 0; while (i<n && !f) { f := (q[i] == x); i := i+1; } result = f; } procedure Insert(x: data) returns result: bool; { havoc i; assume i<n; cnt := 0; result := false; while (cnt<n && !f) { if (q[i]==-1) {q[i] := x; result := true; } else if (q[i]== x) { result := true; } else { i := (i+1) mod n; cnt := cnt+1; } } }

  45. Example 3: Multiset (Insert/Lookup) procedure Lookup(x: data) returns result: bool; { … while (i<n && !f) { f := (q[i] == x); i := i+1; } result = f; } procedure Insert(x: data) returns result: bool; { ... } • Lookup’s that return true commit when they find x. • Lookup’s that return false commit when they read q[0]. • Different reduction proofs needed for the two cases • Replace Lookup(x) with if (*) { Lookup(x); assume result} else { Lookup(x); assume !result} • Use return value as prophecy variable • Perform different reduction proofs on the two branches

  46. Summary • QED: Proof system for concurrent software • Iteration of abstraction and reduction surprisingly powerful • Intricate algorithms verified • Reduction proofs for optimistic concurrency • Actions need to refer to the future: prophecy variables • Annotate actions locally with info about future: tressa • QED + prophecy variables, tressa’s, forward & backward reasoning • Generalized definition of simulation • Soundness proof • Proved examples making use of optimistic concurrency • Future work: Verify transactional memory algorithms, non-blocking data structures.

  47. Multiset 2 3 9 3 5 8 6 8 3 elt ✗ ✗ ✔ ✔ ✔ ✔ ✔ ✔ ✔ vld 49 LookUp (x) for i =1 to n acq (M[i]); if (M[i].elt==x && M[i].vld) rel (M[i]); return true; else rel (M[i]); return false;  M ✗ • Multiset data structure M = { 2, 3, 3, 9, 8, 8, 5 } • Represented by M[1..n] • elt: The element • vld: Is it in the set?

  48. FindSlot and InsertPair InsertPair (x,y) i=FindSlot (x); if (i == -1) { return failure; } j=FindSlot (y); if (j == -1) { M[i].elt= null; return failure; } acq (M[i]) acq (M[j]) M[i].vld = true; M[j].vld = true; rel (M[i]); rel (M[j]); return success; FindSlot (x) r = -1; i = 0; while(i < N && r == -1) { acq (A[i]); if (M[i].elt ==null) { M[i].elt= x; rel (M[i]); r = i; } else { rel (M[i]); } i = i + 1; } return r;

More Related