Refinement Verification of Concurrent Programs and Its Applications
Hongjin LiangUniv. of Science and Technology of China
Advisors: Xinyu Feng and Zhong Shao
Refinement
void main() { print a rectangle;}
void main() { print a square;}
T S: T has no more observable behaviors (e.g. I/O events by print) than S.
Concurrent Program Refinement
• Compilers for concurrent programs
T
S
Compiler
MultithreadedJava programs
Java bytecode
Correct(Compiler):S, T. T = Compiler(S) T S
Concurrent Program Refinement
• Compilers for concurrent programs
• Fine-grained impl. of concurrent objects (libraries)
– E.g. java.util.concurrent
Whole program C[O]
…push(7);x = pop();…
…push(6);…
Client code CConcurrent object O
void push(int v) { local b:=false, x, t; x := new Node(v); while (!b) { t := top; x.next = t; b = cas(&top, t, x); }}
…
int pop() { … …
}
How to specify/prove correctness?
Correctness of Concurrent Objects
• Linearizability [Herlihy&Wing’90]
– O lin S : correctness w.r.t. functionality
– Spec S : abstract object (atomic methods)
– Hard to understand/use
• Equivalent to contextual refinement [Filipovic et al.]
– O ctxt S iff C. C[O] C[S]
…x := 7;push( x );…
…y := pop(); print(y);…
Client C
Concrete obj. O
Abstract obj. S
void push(int v) { … }int pop() { … }
push
pop
O ctxt S iff C. C[O] C[S]
Concurrent Program Refinement
• Compilers for concurrent programs
• Linearizability of concurrent objects (libraries)
• Impl. of software transactional memory (STM)
– Atomic block (transaction) fine-grained impl.
Concurrent Program Refinement
• Compilers for concurrent programs
• Linearizability of concurrent objects (libraries)
• Impl. of software transactional memory (STM)
• Impl. of concurrent garbage collectors (GC)
• Impl. of operating system (OS) kernels
Is such a refinement T S general enough & easy to verify?
(Compositionality)T1 || T2 S1 || S2
T1 S1 T2 S2
Problems with T S
Existing work on verifying T S : either is not compositional, or limits applications.
Long-Standing Problems in Verifying Linearizability
• Objects with Non-Fixed Linearization Points (LPs)
– Future-dependent LPs (e.g. lazy set, pair snapshot)
– Helping (e.g. HSY elimination-backoff stack)
Most existing work : either not supports them, or lacks formal soundness.
Refinement vs. Progress Properties ?
• Linearizability– Correctness w.r.t. functionality– Not talk about termination/liveness properties
• Progress properties– Lock-freedom (LF)– Wait-freedom (WF)– Obstruction-freedom (OF)– Deadlock-freedom (DF)– Starvation-freedom (SF)
Non-blocking impl.
Lock-based impl.
Our Contributions (Part 1)
• RGSim = Rely/Guarantee + Simulation
– Compositional w.r.t. parallel composition
– Flexible & applicable
• optimizations in concurrent contexts
• concurrent GC
• fine-grained concurrent obj.
• …
Our Contributions (Part 2)
• RGSim = Rely/Guarantee + Simulation
• A program logic for linearizability– Support non-fixed LPs
– Verified 12 well-known algorithms (some are used in java.util.concurrent)
– Light instrumentation mechanism to help verification
– Formal meta-theory: simulation (extends RGSim)• Establish a contextual refinement
Our Contributions (Part 3)
• RGSim = Rely/Guarantee + Simulation• A program logic for linearizability
• A framework to characterize progress properties via contextual refinement (CR)– Propose different termination-sensitive CR• Equivalent to linearizability + progress• Unify all five progress properties (LF, WF, OF, DF, SF)
– Make modular verification of whole program C[O] easier
– Potential to have a generic verification framework for linearizability + progress
Outline
• Rely-Guarantee-based simulation for modular verification of concurrent refinement
• Logic for linearizability
• Progress properties and contextual refinement
is NOT compositional w.r.t. parallel composition:
T1 S1 T2 S2
T1 || T2 S1 || S2 T:local t;
t = x;
x = t + 1;
print( x );
S:x++;
print( x );
We have T S, since output(T) output(S) ;
but we do not have T||T S||S .
Existing Proof Methods: Simulation in CompCert
(T, )
(S, ) (S’, ’)
(T’, ’)
* (S’’, ’’)
(T’’, ’’)e
e * …
…
[Leroy et al.]
Source state
Target stateobservable event (e.g. I/O)
T:local t;
t = x;
x = t + 1;
print( x );
S:x++;
print( x );
We have T S , but not T||T S||S
Simulation in CompCert [Leroy et al.]
Can verify refinement of sequential programs
NOT compositional w.r.t. parallel composition
Simulation in CompCert [Leroy et al.]
Can verify refinement of sequential programs
NOT compositional w.r.t. parallel composition Consider NO environments
Simulation in process calculus (e.g. CCS [Milner et al.])
• Assume arbitrary environments
Compositional
Too strong: limited applications
…(T’, ’’) (T’’, ’’’)
(S’, ’’) * (S’’, ’’’) …
’ ’e
e
Assuming Arbitrary Environments
env
env
Too strong to be satisfied, since env. can be arbitrarily bad.
(T, ) (T’, ’)
(S, ) (S’, ’)*
’ ’
Refinement applications have assumptions about S & env.
• Compilers for concurrent programs– Prog. with data races has no semantics (e.g. concurrent C++)– Not guarantee correctness for racy programs
• Fine-grained objects– Accesses use same primitives (e.g. stack: push & pop)– Not guarantee correctness when env. can destroy obj.
• More examples are in the thesis …
Env. of a thread cannot be arbitrarily bad !
[Boehm et al. PLDI’08]
Refinement’s Assumptions
Problems of existing simulations :
Our RGSim :
• Considers no env. in CompCert [Leroy et al.]
NOT compositional w.r.t. parallel composition
• Assumes arbitrary env. in process calculus (e.g. [Milner et al.])
Too strong: limited applications
• Parameterized with the interference with env.
Compositional
More applications
• Use rely/guarantee to specify the interference
[Jones'83]Overview of Rely/Guarantee
• r: acceptable environment transitions• g: state transitions made by the thread
Thread1 Thread2
Nobody else would update x
I guarantee I would not touch y
Nobody else would update y
I guarantee I would not touch xCompatibility (Interference Constraints):
g2 r1 and g1 r2
r1: x = x’
’ r2: y = y’
’
g1: y = y’ ’ g2: x = x’ ’
(T, )
(S, ) (S’, ’)
(T’, ’)
* (S’’, ’’’)
(T’’, ’’’)e
e * …
…
*R
r
G
g
G
g
RGSim = Rely/Guarantee + Simulation
≲ ≲ ≲
(S’, ’’)
(T’, ’’)
≲
(T, r, g) ≲ (S, R, G)
Parallel Compositionality
(T1||T2, r1r2, g1g2) ≲ (S1||S2, R1R2, G1G2)
(T2, r2, g2) ≲ (S2, R2, G2)
(T1, r1, g1) ≲ (S1, R1, G1)
g1 r2 g2 r1 G1 R2 G2 R1
(PAR)
More on Compositionality
(T1, r, g) ≲ (S1, R, G) (T2, r, g) ≲ (S2, R, G)
(T1; T2, r, g) ≲ (S1; S2, R, G)
(T, r, g) ≲ (S, R, G) b B
(while b do T, r, g) ≲ (while B do S, R, G)
An axiomatic proof system for refinement
…
We have applied RGSim to verify …
• Optimizations in parallel contexts– Loop invariant hoisting, strength reduction and induction
variable elimination, dead code elimination, …
• Fine-grained impl. & concurrent objects– Lock-coupling list, counters, Treiber’s non-blocking stack,
concurrent GCD algorithm, …
• Concurrent garbage collectors– A general GC verification framework– Hans Boehm’s concurrent GC [Boehm et al. 91]
Outline
• Rely-Guarantee-based simulation for modular verification of concurrent refinement
• Logic for linearizability
• Progress properties and contextual refinement
Linearizability of Concurrent Objects
• Correctness w.r.t. functionality
• O lin S : Every concurrent execution of object O is “equivalent” to some sequential execution of spec S
[Herlihy&Wing’90]
A concurrent execution of O:
Thread 1:
Thread 2:
retpush(7)
retpush(6)
ret (7)pop()
time
push(6), ret, push(7), ret, pop(), ret(7)
Sequential execution of S
Linearizability of Object O
Linearization point (LP)
Example: Treiber’s Non-Blocking Stack
…v1 next vk next
Top
push(int v):
1 local b:=false, x, t;
2 x := new Node();
3 x.data := v;
4 while(!b){
5 t := Top;
6 x.next := t;
7 b := cas(&Top, t, x);
8 }
next
t
x
v Is it linearizable?
[Treiber’86]
Line 6: the only command that changes the listLP
Not update the shared list“Fixed”: statically
located in impl code
…v1next
vknext
Top
Treiber’s stack O
push(v):
1 local b:=false, x, t;
2 x := new Node(v);
3 while (!b) {
4 t := top;
5 x.next = t;
6 b = cas(&top, t, x);
7 }
PUSH(v): Stk := v::Stk;
Stk = v1 :: v2 :: … :: vk
Abstract stack Slin?
1 local b:=false, x, t;
2 x := new Node(v);
3 while (!b) {
4 t := Top;
5 x.next := t;
push(v):6 b := cas(&Top, t, x);
if (b)
linself;
>
7 }
…v1 vk
Top
v1 :: v2 :: … :: vk
v
next nextStk =v ::
LP
- { [PUSH(v)] … }
- { [] … }
<
Abstract opr is done
Abstract opr PUSH(v) not done
Execute abstract opr simultaneously
Proved it’s LP
Atomic block
Treiber’s stack O Abstract stack Slin?
Basic Approach to Verify O lin S
• Instrument(O) = D with linself at LPs
• Verify D in program logic with rules for linself– New assertions [S] and []– Ensure O’s LP step corresp. to S’s single step
Not support non-fixed LPs– Future-dependent LPs– Helping
Inspired by [Vafeiadis’ Thesis]
Challenge 1: Future-Dependent LP
m 0 1 … k
t2: write(i, d)t1: readPair(i, j)
write(i, d) updates m[i] to a new value d
[Qadeer et al. MSR-TR-2009-142]
readPair(i, j) intends to return snapshot of m[i] and m[j]
Example: Pair Snapshot
Pair Snapshot
v
readPair(int i, j){1 local s:=false, a, b, v, w;2 while (!s) {3 <a := m[i].d; v := m[i].v>;4 <b := m[j].d; w := m[j].v>;5 <if (v = m[i].v) s := true>; 6 }7 return (a, b);}
d
m 0 1 … k
write(int i, d){8 <m[i].d := d; m[i].v++>;}
LP if line 5 succeeds
• Line 4? But line 5 may fail, m[i] and m[j] may be re-read
Where is the LP ?
know: m[i] = (a,v) at line 4
version number
[Qadeer et al. MSR-TR-2009-142]
Future-dependent LPNot supported by linself
readPair(int i, j){1 local s:=false, a, b, v, w;2 while (!s) {3 <a := m[i].d; v := m[i].v;>
4 <b := m[j].d; w := m[j].v;
5 <if (v = m[i].v) { s:= true;
6 }7 return (a, b);}
- { m[i] = (a, v) [RP, (i,j)] … }[RP, (i,j)]
- { s [, (a,b)] s … }
- { m[i] = (a, v) ( [, (a,b)] ) … }
Solution: Try-Commit
>trylinself;
} >commit( [, (a,b)] );
speculate at potential LP, keep both result and original
Challenge 2: Helping
• Example: elimination-backoff stack [Hendler et al. SPAA’04]
• t1 finishes t2’s opr t2’s LP is in the code of t1
• Need to linearize a thread other than self
• New auxiliary command: lin(t)
• New assertions: t S | t
• Details are in the thesis…
Our Approach to Verify O lin S
• Instrument(O) = D with auxiliary cmds at LPs– linself for fixed LPs– try-commit for future-dependent LPs – lin(t) for helping
• Assertions to describe abstract code & statesp, q ::= … | t S | t | p q | p q
• Verify D in our program logic– Extend an existing logic with rules for aux cmds
Our Logic for O lin S
┝ {p (t S)} lin(t) {q * (t )}
┝ {p} S {q}
┝ {p (cid S)} trylinself {( p * (cid S) ) ( q * (cid ) )}
┝ {p} S {q}
┝ {p q} commit(p) {p}
…
More rules and soundness are in the thesis
Verified AlgorithmsObjects Fut. LP Helping Java Pkg
(JUC)
Treiber stack
HSY stack
MS two-lock queue
MS lock-free queue
DGLM queue
Lock-coupling list
Optimistic list
Heller et al lazy list
Harris-Michael lock-free list
Pair snapshot
CCAS
RDCSS
Soundness via Contextual Refinement
O lin S
• “”: all proof methods for ctxt can verify lin
– ctxt is a well-studied concept in PL community(still challenging though)
• “”: modular verification (view C[O] as C[S])– C[S] is simpler to understand/verify
Theorem (equivalence):
O ctxt SProof follows [Filipovic et al., 2009]
Intentional Extensional
Outline
• Rely-Guarantee-based simulation for modular verification of concurrent refinement
• Logic for linearizability
• Progress properties and contextual refinement
Progress Properties
• Describe whether methods eventually return
• Defined similarly to linearizablity– Describe objects’ behaviors instead of clients’– Intentional instead of extensional– E.g. there always exists a method call that’ll return
Can we use contextual refinement to define progress properties?
Our Results
Termination-sensitive contextual refinement
O P S ( iff C. ObsBeh(C[O]) ObsBeh(C[S]) )
Linearizability O lin S
ProgressP(O)
P LF WF OF DF SF
ObsBeh(C[O]) div t-div i-div f-div f-t-div
ObsBeh(C[S]) div t-div div div t-div
Relationships between Progress Properties
Wait-freedom
Lock-freedom
Starvation-freedom
Obstruction-freedom
Deadlock-freedom
+Linearizability
WF
LF SF
OF DF
equiv. to
Conclusion• RGSim = Rely/Guarantee + Simulation– Idea: parameterized with interference with env.– Compositional!– Applications: optimizations, concurrent GC, …
• Program logic for linearizability– Light instrumentation to help verification• linself for fixed LPs• lin(t) for helping• try-commit for future-dependent LPs
– Verified 12 well-known algorithms
Conclusion
• Contextual refinement (CR) framework to unify linearizability + progress– Intentional Extensional– Different correctness properties correspond to
different observable behaviors– Describe effects over clients (useful for modular
verification)– Borrow existing ideas on CR proof to verify
linearizability + progress — future work
Top Related