Compositional Verification of Termination-Preserving Refinement of Concurrent Programs Hongjin Liang...

34
Compositional Verification of Termination-Preserving Refinement of Concurrent Programs Hongjin Liang Univ. of Science and Technology of China (USTC) Joint work with Xinyu Feng (USTC) and Zhong Shao (Yale)

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

Soundness Theorem

If we can find R, G and q such that

then we have: T ⊑p S

{p} (T , S) {q}R, G

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

BACKUP SLIDES

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

Hoffmann et al.’s logic for lock-freedom

• Not prove termination-preservation• Need to know the number of threads

– Token transfer

cnt 0

€ € €

1

succeededfailedfailed

€ €€ € € €

loop: pay one token for one iteration

cas: token transfer

[LICS’13]