Date post: | 18-Dec-2015 |
Category: |
Documents |
Upload: | esther-paul |
View: | 225 times |
Download: | 2 times |
Generalizing Reduction and Abstraction to
Simplify Concurrent Programs:The QED Approach
Shaz QadeerMicrosoft Research
Redmond, WA
Serdar Taşıran, Tayfun Elmas, Ali SezginKoç University Istanbul, Turkey
http://qed.codeplex.com
QED: What is it good for?
Read(X) Write(X) Write(Y) Undo(Y)
Want to verify:- Aborted transactions do not modify their write-set
- Y is not modified here.
- Straightforward if code were sequential
2
QED: What is it good for?
t = Xhavoc(t) Write(X) Write(Y) Undo(Y)Read(X)
Want to verify:- Aborted transactions do not modify their write-set
- Y is not modified here.
- Straightforward if code were sequential
3
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]
4
QED: Simplify (coarsen), then verify
5
. . .
check
P1 PnP2
Correct
Coarser Atomic Actions
6
. . .
check
P1 PnP2
Correct
Difficult to prove• Fine-grain
concurrency• Annotations at every
interleaving point
Easy to prove• Larger atomic blocks• Local, sequential
analysis within atomic blocks
Example: Concurrent increment
7
acquire(lock);
t := x;
t := t + 1;
x := t;
release(lock);
acquire(lock);
k := x;
k := k + 1;
x := k;
release(lock);
x := 0;
assert x == 2;
Thread A Thread B
||
Main thread
Owicki-Gries proof, fine-grain actions
8
L0: acquire(lock);
L1: t := x;
L2: t := t + 1;
L3: x := t;
L4: release(lock);
L5: // end of thread
L0: acquire(lock);
L1: k := x;
L2: k := k + 1;
L3: x := k;
L4: release(lock);
L5: // end of thread
x := 0;
assert x == 2;
||
{ B@L0=>x=0, B@L5=>x=1 }
{ B@L0=>x=0, B@L5=>x=1, held(l,A) }
{ B@L0=>x=0, B@L5=>x=1, held(l,A), t=x }
{ B@L0=>x=0, B@L5=>x=1, held(l,A), t=x+1 }
{ B@L0=>x=1, B@L5=>x=2, held(l,A) }
{ B@L0=>x=1, B@L5=>x=2 }
{ A@L0=>x=0, A@L5=>x=1 }
{ A@L0=>x=0, A@L5=>x=1, held(l,B) }
{ A@L0=>x=0, A@L5=>x=1, held(l,B), k=x }
{ A@L0=>x=0, A@L5=>x=1, held(l,B), k=x+1 }
{ A@L0=>x=1, A@L5=>x=2, held(l,B) }
{ A@L0=>x=1, A@L5=>x=2 }
Thread A Thread B
9
Reduction
inc (): acquire (lock);
t := x;
t := t + 1;
x := t;
release(lock);
Right mover
Both mover
B
B
Left mover
inc (): acquire (lock);
t := x;
t := t + 1;
x := t;
release(lock);
inc (): x := x + 1;
REDUCE-SEQUENTIAL
9
Soundness
10
Soundness theorem:If Pn is correct (satisfies all assertions) then
1. all P1 ≤ i ≤ n are correct. 2. Pn preserves behaviors of all P1 ≤ i ≤ n .
Completeness: Subsumes Owicki-Gries [Nieto, 2007]
. . .
check
P1 PnP2
Correct
QED: Simplifier; complements other methods
11
acquire(lock);
t := x;
t := t + 1;
x := t;
release(lock);
acquire(lock);
k := x;
k := k + 1;
x := k;
release(lock);
x := 0;
assert x == 2;
||
atomic {
acquire(lock);
t := x;
t := t + 1;
x := t;
release(lock);
}
atomic {
acquire(lock);
k := x;
k := k + 1;
x := k;
release(lock);
}
x := 0;
assert x == 2;
||
x := 0;
x := x + 1;
x := x + 1;
assert x == 2;
Simpler Owicki-Gries(4 location invariants)
Correct
Sequential analysis
Correct
Owicki-Gries(12 location invariants)
Correct
QED-verifier
reduceabstract
.....reducecheck
http://qed.codeplex.com
Correct
12
...P1 PnP2
P1
Pn
Proof script Boogie 2, Z3
QEDPLprogram
Automation using SMT solver
VC Valid
13
reduce abstract reduce
check
P1 PnP2
VC Valid VC Valid
CorrectVCValid
. . .
QED Transformations: Abstraction
I,P I,P’
• P’ : Atomic statement [ S ] in P replaced with [ S’ ]
• When?
• When atomic statement [S’] abstracts statement [S]
14
15
QED’s Idea of Abstraction
If for all :
errors1 errors11. If then
s12. If thens2 s1 s2
or errors1
s1
– Going wrong more often is sound for assertion checking
abstracted by
15
16
Flavors of Abstraction
if (x == 1) y := y + 1;
if (*) y := y + 1;
Adding non-determinism
Adding assertions (more “wrong” behaviors)
t := x; havoc t;
assume x != t; skip;
assert (lock_owner == tid);x := t + 1;x := t + 1;
16
“Read abstraction”
QED Transformations: Reduction
[ S1; S2]
[ S1 ; S2 ]
[ S1 ] ; [ S2 ]
[ S1 ] || [ S2 ]
I,P I,P’
17
If [S1] and [S2] are actions of correct mover types
P
P
P’
P’
18
Reduction ;
... 1 2 ... n ; ...
right-mover:
For each execution:
Exist equivalent executions:
... 1 2 ... n ...
... 1 2 ... n ... ...........
... 1 2 ... n ...
;
18
Use of movers in reduction
19
reduce
acquire(lock);
k := x;
k := k + 1;
x := k;
release(lock);
atomic {
acquire(lock);
t := x;
t := t + 1;
x := t;
release(lock);
}
acquire(lock) t := x t := t + 1; release(lock)... ... ......
acquire(lock) t := x t := t + 1 release(lock)... ... ... ......
R B
E1:
E2:
x := t...
x := t
E1 ≈ E2 Reason about only E2
B B L
Right-mover
Both-mover
Both-mover
Both-mover
Left-mover
Mover check in QED: Static, local, semantic
20
...
...
First-order verification condition
For each
;
...
Right-mover ?A
A B ;B A
B :
S1 S2 S3
S1 T2 S3
A B
B A
All actions in programrun by different thread
Traditional use of reduction [Lipton, 1975]
21
S1 S2 S3
acquire y
S1 T2 S3
acquirey
S1 T2 S3
release x
S1 S2 S3
releasex
Right-mover Left-mover
S1 S2 S3
locked access y
S1 T2 S3
locked-accessy
S1 T2 S3
locked access x
S1 S2 S3
locked accessx
Both-mover Both-mover
22
Static mover check
• Static right-mover check between and :
• Simple cases
– Mover check passes:
• and access different variables• and disable each other
– Fails:
• writes to a variable and reads it • and both write to a variable, writes do not commute
22
Reduction: Syntactic to Semantic Notions of Commuting
• Accesses to independent variables• y := 2 and x := z + t;
• Increment and increment• x := x + 1 and x := x + 2
• Acquire: Right mover • Commutes to the right of any action
• But what about
acq(L) acq(L) acq(L) acq(L)
• Both LHS and RHS block• No execution has
two consecutive acq(L)’s
S1 S2 S3
acquire y
S1 T2 S3
acquirey
S1 T2 S3
release x
S1 S2 S3
releasex
Reduction: Normal to Weird Notions of Commuting
• Lock protected accesses by two different threads• p q < q p
• Why do they commute?• q is never followed by p
• How is this captured in QED?
25
Static mover check fails: Apparent conflict
acquire (lock);
t1 := x;
t1 := t1 + 1;
x := t1;
release(lock);
acquire (lock);
t2 := x;
t2 := t2 + 1;
x := t2;
release(lock);
• Static mover check is local, fails!
• Individual actions do not locally contain the information:• “Whenever this action executes, this thread holds the lock”
• Annotate action with local assertion: • Express belief about non-interference
25
26
Auxiliary variable: Which thread holds the lock?
inc (): acquire (lock);
t1 = x;
t1 = t1 + 1
x = t1;
release(lock);
inc (): acquire (lock); a := tid;
t2 = x;
t2 = t2 + 1
x = t2;
release(lock); a := 0;
AUX-ANNOTATE
New invariant: (lock == true) (a != 0)
• Auxiliary variable a is a history variable• Summarizes relevant part of execution history
26
27
Annotating Actions with Assertions
acquire (lock); a := tid;
assert a == tid; t1 = x;
t1 = t1+ 1
assert a == tid; x = t1;
assert a == tid; release(lock); a := 0;
acquire (lock); a := tid;
t1= x;
t1 = t1 + 1
x = t1;
release(lock); a := 0;
ABSTRACT
Invariant: (lock == true) (a != 0)
• Assertions indicate belief about non interference• Annotate actions locally with global information about execution
27
History Variable Annotations Make Static Mover Check Pass
28
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;
R
B
B
B
L
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;
• assert a == tid1; x := t1; and assert a == tid2; x := t2; commute
• α β β α
• Because both α β and β α result in assertion violations.
28
29
Borrowing and paying back assertions
inc (): acquire (lock); a := tid;
assert a == tid; t1 = x;
t1 = t1 + 1
assert a == tid; x = t1;
assert a == tid; release(lock); a := 0;
inc (): acquire (lock); a := tid;
assert a == tid; t1 = x;
t1 = t1 + 1
assert a == tid; x = t1;
assert a == tid; release(lock); a := 0;
REDUCE-SEQUENTIAL, DISCHARGE ASSERTIONS
R
B
B
B
L
Dischargesthe assertions
Invariant: (lock == true) (a != 0)
29
Reduction: Syntactic to Semantic Notions of Commuting
• What else commutes?• Actions that operate on different parts of memory• Different entries of a linked list• Actions on nodes not yet inserted into a data structure
with actions already in the data structure• Currently thread local access with all actions
• Assertions annotate action with reason for non-interference
31
Semantic Reduction: Ruling out Apparent Interference
assert !possiblyInList[t1];t1.next := n1;
assert possiblyInList[p2];n2 := p2.next;
• possiblyInList[t] :
• False when a newly created node assigned to t.
• Set to true when p.next := t for some p. Remains true afterwards.
assert possiblyInList[p2];n2 := p2.next;
assert !possiblyInList[t1];t1.next := n1;
• If p2 and t1 refer to the same node:
• LHS and RHS lead to assertion violations (i.e., not possible)
• Otherwise, no conflict.
31
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 MoverRight 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
Abstraction + Reduction: Increment with CAS
38
t1 := x;s1 := CAS(x,t1,t1+1);
t2 := x;s2 := CAS(x,t2,t2+1);
||
havoc t1;s1 := CAS(x,t1,t1+1);
[ if (*) { s1:=false;
} else { x:=x+1; s1:= true; } ]
38
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]
39
QED and Optimistic Concurrency
• tressa: Mechanism to annotate actions with assertions that can refer to prophecy variables (future)
• assert: Discharged by reasoning about history of execution.
• tressa: Temporal dual of assert
• Example:
y := y+1; z := z-1; assume (x == 0);
40
tressa : Temporal Dual of assert
• Example: y := y+1; // x == 0 or execution blocks z := 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!
41
tressa : Temporal Dual of assert
• Example: y := y+1; // x == 0 or execution blocks z := 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 φ
42
43
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;
REDUCE & RELAX
R
B
B
B
L
Pair Snapshot Example: Write
public void Write(int a, int d)
{
atomic{
m[a].d = d; // Write data
m[a].v++; // Increment version number
}
}
44
if TrySnapshot ends with s == true
TrySnapshot(int a, int b) {
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; }
}
45
a not written to
b not written to
(da,db) isa consistentsnapshot
Other Work on QED
• Variable hiding
• Linearizability-preserving soundness theorem
• Annotation assistance: • Automating proofs for simple programs• Common synchronization idioms
• Verifying parallelizing compilers
46