420 likes | 589 Views
QED: A Simplifier for Concurrent Programs. Shaz Qadeer Microsoft Research. Joint work with Tayfun Elmas Ali Sezgin Serdar Tasiran. Reliable concurrent software?. Concurrency results in Heisenbugs non-deterministic, timing dependent data corruption, crashes
E N D
QED: A Simplifier for Concurrent Programs Shaz Qadeer Microsoft Research Joint work with Tayfun Elmas Ali Sezgin Serdar Tasiran
Reliable concurrent software? • Concurrency results in Heisenbugs • non-deterministic, timing dependent • data corruption, crashes • difficult to detect, reproduce, eliminate • Correctness problem • does program behave correctly for allinputs and allinterleavings?
Undecidable problem! P satisfies S
Assertions: Provide contracts to decompose problem • into a collection of decidable problems • pre-condition and post-condition for each procedure • loop invariant for each loop P satisfies S
Invariant problem • pre x=c; A B int t; L0: acquire(l); L1: t := x; L2: t := t + 1; L3: x := t; L4: release(l); L5: int t; M0: acquire(l); M1: t := x; M2: t := t + 1; M3: x := t; M4: release(l); M5: B@M0x=c,B@M5x=c+1 A@L0x=c, A@L5x=c+1 B@M0x=c,B@M5x=c+1, held(l, A) A@L0x=c, A@L5x=c+1, held(l, B) B@M0x=c,B@M5x=c+1, held(l, A), t=x A@L0x=c, A@L5x=c+1, held(l, B), t=x B@M0x=c,B@M5x=c+1, held(l, A), t=x+1 A@L0x=c, A@L5x=c+1, held(l, B), t=x+1 B@M0x=c+1,B@M5x=c+2, held(l, A) A@L0x=c+1, A@L5x=c+2, held(l, B) B@M0x=c+1,B@M5x=c+2 A@L0x=c+1, A@L5x=c+2 • post x=c+2;
Abstraction problem int t; t := x; t := t + 1; x := t; x := x+1 int t; acquire(l); t := x; t := t + 1; x := t; release(l); ??
Intuitive reasoning with atomic actions • pre x=c; int t; L0: acquire(l); L1: t := x; L2: t := t + 1; L3: x := t; L4: release(l); L5: int t; M0: acquire(l); M1: t := x; M2: t := t + 1; M3: x := t; M4: release(l); M5: • post x=c+2;
Intuitive reasoning with atomic actions • pre x=c; int t; atomic { L0: acquire(l); L1: t := x; L2: t := t + 1; L3: x := t; L4: release(l); } L5: int t; atomic { M0: acquire(l); M1: t := x; M2: t := t + 1; M3: x := t; M4: release(l); } M5: B@M0x=c,B@M5x=c+1 A@L0x=c, A@L5x=c+1 B@M0x=c+1,B@M5x=c+2 A@L0x=c+1, A@L5x=c+2 • post x=c+2;
Intuitive reasoning with atomic actions • pre x=c; atomic { x := x + 1; } atomic { x := x + 1; } • post x=c+2;
Intuitive reasoning with atomic actions • pre x=c; atomic { x := x + 1; } atomic { x := x + 1; } • post x=c+2; Verify using sequential methods!
QED • Do not verify the original program • Instead, simplify the program • Verify the program once it is simple enough I,P Invariant Program text I0,P0 I1,P1 I2,P2 I3,P3 • Simplified program has simpler invariants • Abstraction of a program is another program
Atomic snapshot int[] m; procedure Write(int a, int d) { atomic { m[a] := d; } } procedure Snapshot(int a, int b, out int da, out intdb) { atomic { da := m[a]; db := m[b]; } }
Atomic snapshot class VersionedInteger { int v; int d; } VersionedInteger[] m; procedure Write(int a, int d) { atomic { m[a].d := d; m[a].v := m[a].v+1; } } procedure Snapshot(int a, int b, out bool s, out int da, out intdb) { intva, vb; atomic { va := m[a].v; da := m[a].d; } atomic { vb := m[b].v; db := m[b].d; } s := true; atomic { if (va < m[a].v) { s := false; } } atomic { if (vb < m[b].v) { s := false; } } }
QED-simplified atomic snapshot class VersionedInteger { int v; int d; } VersionedInteger[] m; procedure Write(int a, int d) { atomic { m[a].d := d; m[a].v := m[a].v+1; } } procedure Snapshot(int a: int, int b, out bool s, out int da, out intdb) { atomic { havoc s, da, db; if (s) { da := m[a].d; db := m[b].d; } } }
QED transformations I,P I’,P’ Strengthen invariant Reduce program Abstract program
Rule 1: Strengthen invariant I,P I’,P I’ I
Rule 2: Reduce program I,P I,P’ atomic { A} ; atomic { B } atomic { A ; B }
x release S1 S2 S3 release x S1 T2 S3 acquire y S1 S2 S3 y acquire S1 T2 S3 Right and left movers (Lipton 1975) Lock int owner; procedure acquire() { atomic { assume owner == 0; owner := tid; } } procedure release() { atomic { assert owner == tid; owner := 0; } }
R* . x . N . Y . L* S0 S5 R* . . . Y x . N L* S0 S5 Reduction theorem Sequence R*;(N+); L*is atomic
Rule 3: Abstract program I,P I,P’ atomic { A } atomic { B } From each state x in I, if A can go to y then B can also go to y
QED tool [http://qed.codeplex.com] P1 P2 Pn P1 ... QED Correct Pn reduce abstract ..... reduce check
QED-verified examples • Fine-grained locking • Linked-list with hand-over-hand locking [Herlihy-Shavit 08] • Two-lock queue [Michael-Scott 96] • Non-blocking algorithms • Bakery [Lamport 74] • Non-blocking stack [Treiber86] • Obstruction-free deque [Herlihy et al. 03] • Non-blocking stack [Michael 04] • Writer mode of non-blocking readers/writer lock [Krieger et al. 93] • Non-blocking queue [Michael-Scott 96] • Synchronous queue [Scherer-Lea-Scott 06]
QED transformations I,P I’,P’ • Strengthen invariant • Abstract program • Reduce program • The rules are symbiotic: • Abstraction enables reduction • Reduction enables abstraction • Program simplification enables simpler invariants Together these rules are surprisingly powerful!
Two examples • Atomic snapshot • Abstraction enables reduction • Spin lock • Program simplification yields simpler invariants
class VersionedInteger { int v; int d; } VersionedInteger[] m; procedure Write(int a, int d) { atomic { m[a].d := d; m[a].v := m[a].v+1; } } procedure Snapshot(int a, int b, out bool s, out int da, out intdb) { intva, vb; atomic { va := m[a].v; da := m[a].d; } atomic { vb := m[b].v; db := m[b].d; } s := true; atomic { if (va < m[a].v) { s := false; } } atomic { if (vb < m[b].v) { s := false; } } }
class VersionedInteger { int v; int d; } VersionedInteger[] m; procedure Write(int a, int d) { atomic { m[a].d := d; m[a].v := m[a].v+1; } } procedure Snapshot(int a, int b, out bool s, out int da, out intdb) { intva, vb; atomic { havoc va, da; assume va <= m[a].v; if (va == m[a].v) { da := m[a].d; } } atomic { havoc vb, db; assume vb <= m[b].v; if (vb == m[b].v) { db := m[b].d; } } s := true; atomic { if (va < m[a].v) { s := false; } if (s) { havoc s; } } atomic { if (vb < m[b].v) { s := false; } if (s) { havoc s; } } } Right Mover Right Mover Left Mover Left Mover
class VersionedInteger { int v; int d; } VersionedInteger[] m; procedure Write(int a, int d) { atomic { m[a].d := d; m[a].v := m[a].v+1; } } procedure Snapshot(int a, int b, out bool s, out int da, out intdb) { intva, vb; atomic { havoc va, da; assume va <= m[a].v; if (va == m[a].v) { da := m[a].d; } havoc vb, db; assume vb <= m[b].v; if (vb == m[b].v) { db := m[b].d; } s := true; if (va < m[a].v) { s := false; } if (s) { havoc s; } if (vb < m[b].v) { s := false; } if (s) { havoc s; } } }
class VersionedInteger { int v; int d; } VersionedInteger[] m; procedure Write(int a, int d) { atomic { m[a].d := d; m[a].v := m[a].v+1; } } procedure Snapshot(int a, int b, out bool s, out int da, out intdb) { intva, vb; atomic { havoc va, da, vb, db, s; if (s) { va := m[a].v; da := m[a].d; vb := m[b].v; db := m[b].d; s := true; } } }
class VersionedInteger { int v; int d; } VersionedInteger[] m; procedure Write(int a, int d) { atomic { m[a].d := d; m[a].v := m[a].v+1; } } procedure Snapshot(int a, int b, out bool s, out int da, out intdb) { atomic { havoc da, db, s; if (s) { da := m[a].d; db := m[b].d; } } } Hide va, vb
bool held; procedure acquire() { while (true) { if (CAS(held, false, true)) { break; } } } procedure release() { held := false; } int owner; procedure acquire() { atomic { assume owner == 0; owner := tid; } } procedure release() { atomic { assert owner == tid; owner := 0; } }
bool held; int owner; procedure acquire() { while (true) { if (CAS(held, false, true)) { owner := tid; break; } } } procedure release() { atomic { assert owner == tid; owner := 0; held := false; } }
bool held; int owner; procedure acquire() { while (*) { assume held != false; } atomic { assume held == false; held := true; } owner := tid; } procedure release() { atomic { assert owner == tid; owner := 0; held := false; } }
bool held; int owner; procedure acquire() { while (*) { assume true; } atomic { assume held == false; held := true; } owner := tid; } procedure release() { atomic { assert owner == tid; owner := 0; held := false; } }
bool held; int owner; procedure acquire() { atomic { assume held == false; held := true; } owner := tid; } procedure release() { atomic { assert owner == tid; owner := 0; held := false; } }
bool held; int owner; procedure acquire() { atomic { assume held == false; held := true; } atomic { assert owner == 0; owner := tid; } } procedure release() { atomic { assert owner == tid; owner := 0; held := false; } } Left Mover (Not Quite) Invariant: owner == 0 held == false
bool held; int owner; procedure acquire() { atomic { assume held == false; held := true; assert owner == 0; owner := tid; } } procedure release() { atomic { assert owner == tid; owner := 0; held := false; } }
bool held; int owner; procedure acquire() { atomic { assume held == false; held := true; assert owner == 0; owner := tid; } } procedure release() { atomic { assert owner == tid; owner := 0; held := false; } } Invariant: owner == 0 held == false
bool held; int owner; procedure acquire() { atomic { assume held == false; held := true; assume owner == 0; owner := tid; } } procedure release() { atomic { assert owner == tid; owner := 0; held := false; } }
int owner; procedure acquire() { atomic { assume owner == 0; owner := tid; } } procedure release() { atomic { assert owner == tid; owner := 0; } } Hide held
Conclusions • QED: A simplifier for concurrent programs • Do not verify the original program • Instead, simplify the program • Verify the program once it is simple enough • Other applications • Concurrency testing • Programmer-assisted parallelization