1 / 52

A Calculus of Atomic Actions

A calculus of atomic actions problem, highlighting verifying assertions in concurrent programs through a proof method known as QED. Our approach focuses on local conditions, program invariants, data integrity, and absence of null pointer dereferences. We use the QED tool, emphasizing atomicity for separation of concerns and concurrency handling. Proof by reduction techniques, including action patterns and abstraction, are employed for successful assertion validation in concurrent programming. The application of QED proof rules and program transformations is central to our approach, ensuring soundness and validation of program assertions.

philiplopez
Télécharger la présentation

A Calculus of Atomic Actions

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. Serdar Tasiran Koc University, Istanbul, Turkey Tayfun Elmas Shaz Qadeer Koc University Microsoft Research A Calculus of Atomic Actions

  2. Problem • Verifying assertions in concurrent programs • Local conditions, program invariants, data integrity, absence of null pointer dereferences, ... static void transfer(Account from, Account to, int amount) { assert (from != null && to != null); assert (from.balance >= amount); old_total := from.balance + to.balance; tmp := from.balance; from.balance := tmp – amount; tmp := to.balance; to.balance := tmp + amount; assert (from.balance + to.balance == old_total); }

  3. Our Approach: QED • Proof method for verifying assertions • Implementation: The QED tool • Central idea: Atomicity • Allows separation of concerns • Concurrency: Non-interference, synchronization mechanism • Data: Sequential reasoning within atomic blocks to prove assertions • QED proof rules: Program transformation steps • Abstraction: To increase “non-interference” between actions • Reduction: Prove bigger blocks atomic • Grow atomic blocks • Sequential reasoning within atomic blocks

  4. Outline • Motivating example • Proof method • Experience • Full example: Multiset

  5. Example – inc () Main: assume (x == 0); inc() || inc() assert (x == 2) inc (): int t; acquire (lock); t = x; t = t + 1; x = t; release(lock);

  6. Proof by Owicki-Gries A: <B@L0=>x=0, B@L5=>x=1> L0: acquire(l); <B@L0=>x=0, B@L5=>x=1, held(l,A)> L1: t := x; <B@L0=>x=0, B@L5=>x=1, held(l,A), t=x> L2: t := t + 1; <B@L0=>x=0, B@L5=>x=1, held(l,A), t=x+1> L3: x := t; <B@L0=>x=1, B@L5=>x=2, held(l,A)> L4: release(l) <B@L0=>x=1, B@L5=>x=2> B: <A@L0=>x=0, A@L5=>x=1> L0:acquire(l); <A@L0=>x=0, A@L5=>x=1, held(l,B)> L1:t := x; <A@L0=>x=0, A@L5=>x=1, held(l,B), t=x> L2:t := t + 1; <A@L0=>x=0, A@L5=>x=1, held(l,B), t=x+1> L3:x := t; <A@L0=>x=1, A@L5=>x=2, held(l,B)> L4:release(l) <A@L0=>x=1, A@L5=>x=2> ||

  7. Reduction • α right mover (R) if for every execution ξ = ……. α β …….ξ’ = ……. β α ……. is equivalent to ξ • Static check for α being a right mover: • For all actions β in program, is α β always “simulated” by β α? • Easy case: β cannot follow α • Example: α = β = lock.acquire() • Reduction: Atomic patterns • RRRR N LLLLL • RRRRR • LLLLL • ......

  8. Proof by reduction - 1 inc (): int t; acquire (lock); t = x; t = t + 1 x = t; release(lock); inc (): int t; acquire (lock); t = x; t = t + 1 x = t; release(lock); R B REDUCE-SEQUENTIAL B B L inc (): x = x + 1;

  9. Proof by reduction - 2 assume (x == 0); x = x + 1; x = x + 1; assert (x == 2) assume (x == 0); inc() || inc() assert (x == 2) assume (x == 0); x = x + 1 || x = x + 1 assert (x == 2) B B REDUCE-PARALLEL INLINE-CALL

  10. Non-blocking increment inc (): int t; while(true) { t = x; if(CAS(x, t, t+1)) break; } CAS(x, t, t + 1): □ assume (x == t); x = t + 1; return true; assume (x != t); return false;

  11. Abstraction for reduction inc (): int t; while(*) { t = x; assume x != t; } t = x; assume x == t; x = t + 1; inc (): int t; while(true) { t = x; if(CAS(x, t, t+1)) break; }

  12. Abstraction for reduction inc (): int t; while(*) { havoc t; skip; } havoc t; assume x == t; x = t + 1; inc (): int t; while(*) { t = x; assume x != t; } t = x; assume x == t; x = t + 1; SIMULATE SIMULATE SIMULATE

  13. Proof by reduction inc (): int t; while(*) { havoc t; skip; } havoc t; assume x == t; x := t + 1; inc (): int t; havoc t; havoc t; x := x + 1; REDUCE-LOOP B

  14. ActionPL Syntax: Procedure bodies Global variables Program’s main body Program:

  15. Gated actions Gate: Assertion on pre-state Transition: Two-store relation x = x + 1; assert (x != 0); y = y / x; x = x + 1; assert (x != 0); y = y / x;

  16. Semantics of ActionPL Current dynamic statement Current store Execution: Atomic transitions: Pre- and post-store satisfies transition Pre-store violates assertion Pre-store satisfies assertion

  17. Operational semantics

  18. Proof method Proof context Program invariant (proof ensures this) Each execution starts from invariant Each transition preserves invariant Current program • Proof step: Governed by a proof rule • May strengthen invariant • May rewrite program

  19. Soundness • Theorem [Preservation] • Each proof step: • Let . If goes from to then • goes from to , or • goes wrong from • Theorem [Soundness] • Each proof: • If goes from to such that then: • goes from to , or • goes wrong from skip, error,other dyn. stmt.

  20. Validating assertions • Proof succeeds if • For all in , holds. • Verifying each assertion is local & sequential

  21. Invariants Each action either goes wrong or preserves new invariant New invariant is stronger true (y >= 0) Does not touch y x = y; x = x + 1; x = y; x = x + 1; Preserves invariant if assertion is not violated assert (x > 0); y = y / x; assert (x > 0); y = y / x;

  22. Validating assertions - example (y >= 0)  (y > -1) (y >= 0) (y >= 0) (y >= 0) x = y; x = x + 1; assert (y > -1); x = y; x = x + 1; assert (x > 0); y = y / x; assert (true); x = y; x = x + 1; y = y / x; assert (x > 0); y = y / x;

  23. Auxiliary variables inc (): int t; acquire (lock); a := tid; t := x; t := t + 1 x := t; release(lock); a := 0;

  24. Auxiliary variables 24 We add an auxiliary variable We may modify all actions New transition modifies new aux. variable New transitions preserve invariant Auxiliary variable does not affect rest of transition predicate, cannot block.

  25. Simulation From each store satisfying invariant, goes wrong or simulates . New action preserves invariant New action simulates the former Adding behaviors that go wrong Adding non-determinism assert (x > 0); y = y / x; if (x == 1) y = y + 1; if (*) y = y + 1; y = y / x;

  26. Read abstraction inc (): int t; while(*) { havoc t; skip; } havoc t; assume x == t; x = t + 1; inc (): int t; while(*) { t = x; assume x != t; } t = x; assume x == t; x = t + 1; SIMULATE SIMULATE

  27. Abstraction with assertions inc (): int t; acquire (lock); a := tid; t := x; t := t + 1 x := t; release(lock); a := 0; inc (): int t; acquire (lock); t := x; t := t + 1 x := t; release(lock); AUX-ANNOTATE

  28. Abstraction with assertions inc (): int t; acquire (lock); a := tid; assert a == tid; t := x; assert a == tid; t := t + 1 assert a == tid; x := t; assert a == tid;release(lock); a := 0; inc (): int t; acquire (lock); a := tid; t := x; t := t + 1 x := t; release(lock); a := 0; SIMULATE

  29. Abstraction with assertions Discharges the assertions inc (): int t; acquire (lock); a := tid; assert a == tid;t := x; assert a == tid;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; assert a == tid; t := t + 1 assert a == tid; x := t; assert a == tid;release(lock); a := 0; R B B B L REDUCE & RELAX • “Borrowing assertions”: • Introduce assertions, use them to prove larger blocks atomic • Discharge later, when atomic blocks are large enough.

  30. Reduction Moving to the left makes the execution either - go to same end state or - makes execution go wrong Actions executed by different threads

  31. Reduce - sequential

  32. Reduce – nondet. choice CAS(x, t, t + 1): CAS(x, t, t + 1): □ □ assume (x == t); x = t + 1; return true; assume (x != t); return false; assume (x == t); x = t + 1; return true; assume (x != t); return false; DIV(x, y): DIV (x, y): □ assert (x != 0 && y != 0); assert (x != 0); y = y / x; return y; assert (y != 0); x = x / y; return x; □ y = y / x; return y; x = x / y; return x;

  33. Reduce - loop Specification preserves invariant Loop body is either left or right mover Specification simulates zero or more iterations Specification is reflexive

  34. Reduce - parallel Explicitly expand the parallel statement to its possible executions Reduces parallel composition to sequential composition

  35. Experience • Implementation: Spec#  BoogiePL  QED • Generate verification condition (VC) for validity of each proof step • VC fed to the Z3 theorem prover • Purity benchmarks [Flanagan et.al, 2005] • Non-blocking algorithms • Obstruction-free deque [Herlihy et.al. 2003] • Non-blocking stack [Michael, 2004] • Bakery [Lamport, 1974] • Multiset • Conclusion: • Iteration of abstraction and reduction is powerful • Growing atomic code blocks useful approach 35

  36. Multiset 2 9 3 5 3 8 6 8 3 elt          vld 36 • 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? 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 

  37. 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;

  38. Rewriting the “if” statement 38 FindSlot (x) r = -1; i = 0; while(i < N && r == -1) { acq (A[i]); assume (M[i].elt ==null); M[i].elt= x; rel (M[i]); r = i; acq (A[i]); assume (M[i] != null); rel (M[i]); i = i + 1; } return r; 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; □

  39. mutex (M[i].lock == true) 39 FindSlot (x) r = -1; i = 0; while(i < N && r == -1) { acq (A[i]); assume (M[i].elt ==null); M[i].elt= x; rel (M[i]); r = i; acq (A[i]); assume (M[i] != null); rel (M[i]); i = i + 1; } return r; FindSlot (x) r = -1; i = 0; while(i < N && r == -1) { acq (A[i]); assume (M[i].elt ==null); M[i].elt= x; rel (M[i]); r = i; acq (A[i]); assume (M[i] != null); rel (M[i]); i = i + 1; } return r; REDUCE □ □

  40. Read abstraction 40 FindSlot (x) r = -1; i = 0; while(i < N && r == -1) { acq (A[i]); assume (M[i].elt ==null); M[i].elt= x; rel (M[i]); r = i; acq (A[i]); skip rel (M[i]); i = i + 1; } return r; FindSlot (x) r = -1; i = 0; while(i < N && r == -1) { acq (A[i]); assume (M[i].elt ==null); M[i].elt= x; rel (M[i]); r = i; acq (A[i]); assume (M[i] != null); rel (M[i]); i = i + 1; } return r; □ □ SIMULATE

  41. Rewriting 41 FindSlot (x) r = -1; i = 0; while(i < N && r == -1) { acq (A[i]); assume (M[i].elt ==null); M[i].elt= x; rel (M[i]); r = i; acq (A[i]); skip; rel (M[i]); i = i + 1; } return r; FindSlot (x) r = -1; i = 0; while(i < N && r == -1) { acq (A[i]); assume (M[i].elt ==null); M[i].elt= x; rel (M[i]); r = i; skip i = i + 1; } return r; □ □

  42. Reducing loop 42 FindSlot (x) r = -1; i = 0; havoc i; assume (0 <= i < N); assume (M[i].elt == null); M[i].elt = x; r = i; skip; return r; FindSlot (x) r = -1; i = 0; while(i < N && r == -1) { acq (A[i]); assume (M[i].elt ==null); M[i].elt= x; rel (M[i]); r = i; skip i = i + 1; } return r; R REDUCE-LOOP □ □

  43. Rewriting 43 FindSlot (x) i = 0; havoc i; assume (0 <= i < N); assume (M[i].elt == null); M[i].elt = x; return i; return -1; FindSlot (x) r = -1; i = 0; havoc i; assume (0 <= i < N); assume (M[i].elt == null); M[i].elt = x; r = i; skip; return r; □ □

  44. FindSlot 44 FindSlot (x) i = 0; havoc i; assume (0 <= i < N); assume (M[i].elt == null); M[i].elt = x; return i; return -1; Invariant: (0 <= i < N): (M[i].elt == null)  (M[i].vld == false) □

  45. mutex (M[i].elt != null && M[i] == false) 45 InsertPair (x,y) i =FindSlot (x); if (i == -1) return failure; a[i] = tid; j =FindSlot (y); if (j == -1) M[i].elt= null; return failure; a[j] = tid; acq (M[i]) acq (M[j]) M[i].vld = true; M[j].vld = true; rel (M[i]); rel (M[j]); return success; 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; R R SIMULATE

  46. Reduce 46 InsertPair (x,y) i =FindSlot (x); if (i == -1) return failure; a[i] = tid; j =FindSlot (y); if (j == 0) M[i].elt= null; return failure; a[j] = tid; acq (M[i]) acq (M[j]) M[i].vld = true; M[j].vld = true; rel (M[i]); rel (M[j]); return success; InsertPair (x,y) i =FindSlot (x); if (i == -1) return failure; a[i] = tid; j =FindSlot (y); if (j == 0) M[i].elt= null; return failure; a[j] = tid; acq (M[i]) acq (M[j]) M[i].vld = true; M[j].vld = true; rel (M[i]); rel (M[j]); return success; R R REDUCE-SEQ.

  47. Future work • Proof script templates for encoding synchronization idioms • Guidelines for adding annotation and code transformations • Mutual-exclusion, readers/writers lock, barriers • Barriers, event synchronization • Optimistic concurrency • Statically checking serializability of marked-atomic blocks • Verifying STM implementations using QED 47

More Related