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)

Upload: christian-cook

Post on 26-Dec-2015

227 views

Category:

Documents


0 download

TRANSCRIPT

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]