QED: A Simplifier for Concurrent Programs Shaz Qadeer Microsoft Research Joint work with Tayfun ElmasAli SezginSerdar Tasiran.

Presentation on theme: "QED: A Simplifier for Concurrent Programs Shaz Qadeer Microsoft Research Joint work with Tayfun ElmasAli SezginSerdar Tasiran."— Presentation transcript:

QED: A Simplifier for Concurrent Programs Shaz Qadeer Microsoft Research Joint work with Tayfun ElmasAli SezginSerdar 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 all inputs and all interleavings?

P satisfies S 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

int t; L0: acquire(l); L1: t := x; L2: t := t + 1; L3: x := t; L4: release(l); L5: pre x=c; post x=c+2; int t; M0: acquire(l); M1: t := x; M2: t := t + 1; M3: x := t; M4: release(l); M5: AB B@M0  x=c, B@M5  x=c+1 B@M0  x=c, B@M5  x=c+1, held(l, A) B@M0  x=c, B@M5  x=c+1, held(l, A), t=x B@M0  x=c, B@M5  x=c+1, held(l, A), t=x+1 B@M0  x=c+1, B@M5  x=c+2, held(l, A) B@M0  x=c+1, B@M5  x=c+2 A@L0  x=c, A@L5  x=c+1 A@L0  x=c, A@L5  x=c+1, held(l, B) A@L0  x=c, A@L5  x=c+1, held(l, B), t=x A@L0  x=c, A@L5  x=c+1, held(l, B), t=x+1 A@L0  x=c+1, A@L5  x=c+2, held(l, B) A@L0  x=c+1, A@L5  x=c+2 Invariant problem

int t; acquire(l); t := x; t := t + 1; x := t; release(l); Abstraction problem int t; t := x; t := t + 1; x := t; x := x+1 ??

int t; L0: acquire(l); L1: t := x; L2: t := t + 1; L3: x := t; L4: release(l); L5: pre x=c; post x=c+2; int t; M0: acquire(l); M1: t := x; M2: t := t + 1; M3: x := t; M4: release(l); M5: Intuitive reasoning with atomic actions

int t; atomic { L0: acquire(l); L1: t := x; L2: t := t + 1; L3: x := t; L4: release(l); } L5: pre x=c; post x=c+2; int t; atomic { M0: acquire(l); M1: t := x; M2: t := t + 1; M3: x := t; M4: release(l); } M5: Intuitive reasoning with atomic actions B@M0  x=c, B@M5  x=c+1 B@M0  x=c+1, B@M5  x=c+2 A@L0  x=c, A@L5  x=c+1 A@L0  x=c+1, A@L5  x=c+2

pre x=c; post x=c+2; atomic { x := x + 1; } Intuitive reasoning with atomic actions atomic { x := x + 1; }

pre x=c; post x=c+2; atomic { x := x + 1; } Intuitive reasoning with atomic actions Verify using sequential methods!

Do not verify the original program Instead, simplify the program Verify the program once it is simple enough QED I 0,P 0 I 1,P 1 I 2,P 2 I 3,P 3 I,P InvariantProgram text Simplified program has simpler invariants Abstraction of a program is another program

procedure Write(int a, int d) { atomic { m[a] := d; } } procedure Snapshot(int a, int b, out int da, out int db) { atomic { da := m[a]; db := m[b]; } } Atomic snapshot int[] 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 int db) { int va, 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; } } } Atomic snapshot class VersionedInteger { int v; int d; } VersionedInteger[] m;

QED-simplified atomic snapshot 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 int db) { atomic { havoc s, da, db; if (s) { da := m[a].d; db := m[b].d; } } class VersionedInteger { int v; int d; } VersionedInteger[] m;

QED transformations I,PI’,P’ 1.Strengthen invariant 2.Reduce program 3.Abstract program

Rule 1: Strengthen invariant I,PI’,P I’  I

Rule 2: Reduce program atomic { A ; B }atomic { A } ; atomic { B } I,PI,P’

S1S1 S2S2 S3S3 acquirey S1S1 T2T2 S3S3 y S1S1 T2T2 S3S3 releasex S1S1 S2S2 S3S3 x Right and left movers (Lipton 1975) int owner; procedure acquire() { atomic { assume owner == 0; owner := tid; } procedure release() { atomic { assert owner == tid; owner := 0; } Lock

S0S0. S5S5 R* N L*xY... S0S0. S5S5 R* N L* x Y... Reduction theorem Sequence R*;(N+  ); L* is atomic

Rule 3: Abstract program atomic { A }atomic { B } I,PI,P’ From each state x in I, if A can go to y then B can also go to y

QED tool reduce abstract..... reduce check [http://qed.codeplex.com] QED Correct 21... P1P1 PnPn P2P2 P1P1 PnPn

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 [Treiber 86] – 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,PI’,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

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 int db) { int va, 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 int db) { int va, 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; } } } Left Mover Right 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 int db) { int va, 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 int db) { int va, 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 int db) { atomic { havoc da, db, s; if (s) { da := m[a].d; db := m[b].d; } Hide va, vb

Spin Lock

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

Download ppt "QED: A Simplifier for Concurrent Programs Shaz Qadeer Microsoft Research Joint work with Tayfun ElmasAli SezginSerdar Tasiran."

Similar presentations