Compositional Verification of Termination-Preserving Refinement of Concurrent Programs Hongjin Liang...
-
Upload
christian-cook -
Category
Documents
-
view
226 -
download
0
Transcript of Compositional Verification of Termination-Preserving Refinement of Concurrent Programs Hongjin Liang...
Compositional Verification of Termination-Preserving Refinement of
Concurrent Programs
Hongjin LiangUniv. of Science and Technology of China (USTC)
Joint work with Xinyu Feng (USTC) and Zhong Shao (Yale)
Applications of Refinement of Concurrent Programs
• Correctness of program transformations– Compilers, program optimizations, …
T
S
Compiler
MultithreadedJava programs
Java bytecode
Refinement T ⊑ S : T has no more
observable behaviors than S
Applications of Refinement of Concurrent Programs
• Correctness of program transformations– Compilers, program optimizations, …
• Correctness of concurrent objects & libraries
Example – Counter Atomic spec.
inc(){ local done, tmp; done = false; while (!done) { tmp = cnt; done = cas(cnt, tmp, tmp+1); }}
INC(){ <CNT++>}
atomic block
Impl. in concurrent setting
inc || while(true) inc INC || while(true) INC⊑
Correctness: impl refines atomic spec in any contexte.g.
Applications of Refinement of Concurrent Programs
• Correctness of program transformations– Compilers, program optimizations, …
• Correctness of concurrent objects & libraries
• Correctness of runtime systems & OS kernels– Concurrent garbage collectors, STM algorithms, …
Refinement needs to preserve termination!
• while (true) skip ⋢ skip
• Compilers of concurrent programs– Need to preserve termination
• Concurrent objects & libraries– Must satisfy functionality correctness (e.g. linearizability)
& “termination” properties (e.g. lock-freedom)
No existing logics can verify such a refinement T ⊑ S !
Our Contributions
• Rely-Guarantee-based program logic– For termination-preserving refinement– Compositional verification
• Applied to verify– Linearizability + lock-freedom– Correctness of optimizations of concurrent prog.
• New simulation as meta-theory
Termination-Preserving Refinement T ⊑ S : More Examples
while (true) skip skip⋢
while (true) skip;print(1);
print(1);while (true) skip;⋢
We should avoid T looping infinitely without progresscannot print out 1 always print out 1
complete some abstract tasks of S
Verifying T ⊑ S : How to avoid T looping infinitely without progress
• Assign tokens for loops– Consume a token for each iteration
€
One token to pay for one iterationlocal i = 0;
while (i < 2) i++;print(1);
print(1);⊑€
i = 0i = 1i = 2
Verifying T ⊑ S : How to avoid T looping infinitely without progress
• Assign tokens for loops– Consume a token for each iteration
local i = 0;while (true) i++;print(1);
print(1);⋢Never enough tokens!
How to compositionally verify T ⊑ Sin concurrent settings
(Compositionality)T1 || T2 ⊑
S1 || S2
T1 S1⊑
T2 S2⊑
In particular, termination-preservation of individual threads is not compositional
Challenge : termination-preservation of individual threads is not compositional
print(1); || print(1);S1 || S2 :
while (i==0) { i++;}print(1);
||while (i==0) { i--;}print(1);
T1 || T2 :
Both T1 & T2 terminate, but T1 || T2 may not terminate
Challenge : termination-preservation of individual threads is not compositional
• Consider NO environment
Need to take env interference into account!How will env affect termination-preservation? Which effects are acceptable? How to change tokens for acceptable env effects?
To compositionally verify T ⊑ S in concurrent settings, …
Env may delay progress of the current thread. Is it always bad?
Env delays progress of current thread – Sometimes acceptable
inc(){1 local done, tmp;2 done = false;3 while (!done) {4 tmp = cnt;5 done = cas(cnt, tmp, tmp+1);6 }}
while(true) inc;||
INC(){ <CNT++> }
Take a snapshotCompare and swap
May never progress (complete abstract task INC) since its cas fails infinitely often
Env delays progress of current thread – Sometimes acceptable
inc(){1 local done, tmp;2 done = false;3 while (!done) {4 tmp = cnt;5 done = cas(cnt, tmp, tmp+1);6 }}
while(true) inc;||
INC(){ <CNT++> }Because: The failure of one thread must be caused by the success of another. The system as a whole progresses.
Should allow current thread to loop more (i.e. reset tokens) if env progresses
Our Ideas
• Prove termination-preservation of individual threads under env interference
– Assign tokens for loops
– Consume a token for each iteration• Avoid looping infinitely without progress
– Could reset tokens when env progresses• The system as a whole progresses. So the current thread
can loop more.
How tokens change : Example of counter
inc(){1 local done, tmp;2 done = false;3 while (!done) {4 tmp = cnt;5 done = cas(cnt, tmp, tmp+1);6 }}
cnt 0
inc || inc || inc ⊑ INC || INC || INC
€ € €
1
succeededfailedfailed
pay for the next iteration
Idea: if env progresses, reset the token.
Detailed Thread-Local Proof of Counter
inc(){
1 local done, tmp;2 done = false;3 while (!done) {4 tmp = cnt;5 done = cas(cnt, tmp, tmp+1);6 }
}
INC(){ <CNT++>}
{ cnt = CNT }
{ cnt = CNT rem(skip) }
rem(INC) }
abstract tasks to be finished
Embed hi-level code in assertions
inc(){1 local done, tmp;2 done = false;
3 while (!done) {
4 tmp = cnt;
5 done = cas(cnt, tmp, tmp+1);
6 }}
{ cnt = CNT rem(INC) }
Assign tokens for loops
}€
{ cnt = CNT rem(INC) }pay for one
iteration
{ cnt = CNT rem(INC) ( cnt = tmp )} cnt ≠ tmp
{ cnt = CNT+1 (done rem(INC)) … }
env’s progress – reset tokens
) }€
execute hi-level code
{ cnt = CNT (done rem(skip)) … }(done rem(INC) ) }€
pay for the next iteration
NOT a total correctness logic!
• We ensure low-level preserves termination of high-level– For loops: ensure progress before consuming all tokens
• Not ensure termination of low-level/high-level code– For loops: not ensure termination before consuming all tokens
INC_LOOP(){ while (true) { <CNT++>; }}
inc_loop(){ local tmp; while (true) { tmp = cnt; cas(cnt, tmp, tmp+1); }}
Example – loop a counter
⊑
INC_LOOP(){ while (true) { INC; }}
inc_loop(){1 local tmp;
2 while (true) {
3 tmp = cnt;
4 cas(cnt, tmp, tmp+1);
5 }}
{ cnt = CNT rem(INC_LOOP) } }€
{ cnt = CNT rem(INC_LOOP) }
{ cnt = CNT rem(INC_LOOP) … }
pay for the next iteration
env’s progress – reset tokens
{ cnt = CNT rem(INC_LOOP) (cnt = tmp cnt ≠ tmp )}€
{ cnt = CNT+1 rem(INC_LOOP) … }
if cas successful
€ reset tokenssince the thrdprogresses
rem(INC; INC_LOOP) …} execute hi-level code
Need one token only
Summary of Our Informal Ideas
• Prove termination-preservation of individual threads under env interference
– Assign tokens for loops
– Consume a token for each iteration
– Could reset tokens when env progresses
– Could reset tokens when current thread progresses
Our Logic for Verifying T ⊑ S
• Judgment
– R, G: rely/guarantee to describe env interference
… and extended with effects on termination-preservation (i.e. whether current thrd is allowed to reset tokens)
R, G {p rem(S) } T {q rem(skip)}
{p rem(S)} T {q rem(skip)}R, G{p} (T , S) {q}R, G
If we can find R, G and q such that
then we have: T ⊑p S
{p} (T , S) {q}R, G
Compositionality Rules
{p} (T1 , S1) {r}R, G {r} (T2 , S2) {q}R, G
{p} (T1;T2 , S1;S2) {q}R, G
Just like standard Rely/Guarantee rules, e.g.
{p1} (T1 , S1) {q1}R1, G1{p2} (T2 , S2) {q2}R2, G2
{p1p2} (T1ǁT2 , S1ǁS2) {q1q2}R1R2, G1G2
G2 R1G1 R2
Applications
• Linearizability & lock-freedom– Counters and its variants– Treiber stack– Michael-Scott lock-free queue– DGLM lock-free queue
• Correctness of non-atomic objects– Synchronous queue (used in Java 6)
• Equivalence of optimized algo & original one– Counters and its variants– TAS lock and TTAS lock
Conclusion
• Logic for termination-preserving refinement of concurrent programs– Use tokens for termination-preservation
• Reset tokens when current thrd or env progresses• New rely/guarantee conditions for env interference
– Compositionality
– Meta-theory: a new simulation
http://kyhcs.ustcsz.edu.cn/relconcur/rgsimt
Examples that we cannot verify
• Linearizability & lock-freedom– Helping
• HSY elimination-backoff stack
– Speculation• RDCSS algorithm
• Technically, we use a forward simulation as meta-theory– Forward-backward simulation?
Lock-freedom vs. Obstruction-freedom vs. Wait-freedom
How is progress of current thread affected by env?
• Lock-freedom– Can be affected only if env progresses
• Obstruction-freedom– Can be affected whatever env does– But when the thread executes in isolation, it must progress
• Wait-freedom– Cannot be affected
Modify our logic to verify obstruction-freedom and wait-freedom
• Our logic: distinguish whether env progresses– Could reset tokens when env progresses– Lock-freedom
• Obstruction-freedom– Could reset tokens whenever env does– But when no env, the thread must progress
• Wait-freedom– Never reset tokens
• Should also modify definition of T ⊑ S for obstruction-freedom and wait-freedom
Lock-Based Algorithms
• Refinement needs to take fairness into account
inc(){ lock(l); cnt++; unlock(l);}
INC(){ <CNT++> }
inc || inc may not terminate in an unfair execution