340 likes | 513 Views
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). Applications of Refinement of Concurrent Programs.
E N D
Compositional Verification of Termination-Preserving Refinement of Concurrent Programs Hongjin Liang Univ. of Science and Technology of China (USTC) Joint work with XinyuFeng (USTC) and ZhongShao (Yale)
Applications of Refinement of Concurrent Programs • Correctness of program transformations • Compilers, program optimizations, … S Multithreaded Java programs Refinement T ⊑ S : T has no more observable behaviors than S Compiler T Java bytecode
Applications of Refinement of Concurrent Programs • Correctness of program transformations • Compilers, program optimizations, … • Correctness of concurrent objects & libraries
Example – Counter Impl. in concurrent setting Atomic spec. atomic block inc(){ local done, tmp; done = false; while (!done) { tmp = cnt; done = cas(cnt, tmp, tmp+1); } } INC(){ <CNT++> } Correctness: implrefines atomic spec in any context e.g. inc||while(true) inc ⊑ INC||while(true) INC
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 RefinementT ⊑S : More Examples • while (true) skip • ⋢ • skip • while (true) skip; • print(1); • print(1); • while (true) skip; • ⋢ cannot print out 1 always print out 1 We should avoid T looping infinitely without progress • 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 iteration • local i = 0; • while (i < 2) • i++; • print(1); • ⊑ • print(1); € € i = 1 i = 2 i = 0
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 T1 ⊑ S1 ⊑ S2 T2 (Compositionality) T1 || T2 ⊑ S1 || S2 In particular, termination-preservation of individual threads is notcompositional
Challenge : termination-preservation of individual threads is notcompositional || print(1); print(1); S1 ||S2: while (i==0) { i--; } print(1); T1 ||T2 : while (i==0) { i++; } print(1); || Both T1 & T2 terminate, but T1 ||T2may not terminate
Challenge : termination-preservation of individual threads is notcompositional • Consider NO environment To compositionally verify T ⊑S in concurrent settings, … Need to takeenv interference into account! How will env affect termination-preservation? Which effects are acceptable? How to change tokens for acceptable env effects? • Env may delay progress of the current thread. Is it always bad?
Envdelays progress of current thread – Sometimes acceptable INC(){ <CNT++> } inc(){ 1 local done, tmp; 2 done = false; 3 while (!done) { 4tmp = cnt; 5 done = cas(cnt, tmp, tmp+1); 6 } } Take a snapshot Compare and swap || while(true) inc; May never progress (complete abstract task INC) since its cas fails infinitely often
Envdelays progress of current thread – Sometimes acceptable Because: The failure of one thread must be caused by the success of another. The system as a whole progresses. INC(){ <CNT++> } inc(){ 1 local done, tmp; 2 done = false; 3 while (!done) { 4tmp = cnt; 5 done = cas(cnt, tmp, tmp+1); 6 } } || while(true) inc; 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 envprogresses • The system as a whole progresses. So the current thread can loop more.
How tokens change : Example of counter inc|| inc|| inc⊑INC|| INC || INC pay for the next iteration failed failed succeeded € € € • Idea: if env progresses, reset the token. inc(){ 1 local done, tmp; 2 done = false; 3 while (!done) { 4tmp = cnt; 5 done = cas(cnt, tmp, tmp+1); 6 } } 0 1 cnt
Detailed Thread-Local Proof of Counter abstract tasks to be finished • inc(){ • 1 local done, tmp; • 2 done = false; • 3 while (!done) { • 4tmp = cnt; • 5 done = cas(cnt, tmp, tmp+1); • 6 } • } INC(){ <CNT++> } { cnt = CNT } rem(INC) } Embed hi-level code in assertions { cnt = CNT rem(skip) }
inc(){ • 1 local done, tmp; • 2 done = false; • 3 while (!done) { • 4tmp = cnt; • 5 done = cas(cnt, tmp, tmp+1); • 6 } • } Assign tokens for loops { cnt = CNT rem(INC) } pay for one iteration { cnt = CNT rem(INC)} env’s progress – reset tokens • cnt≠ tmp { cnt = CNT rem(INC) ( cnt = tmp )} execute hi-level code } ) } € € pay for the next iteration { cnt = CNT+1 (done rem(INC)) … } { cnt = CNT (done rem(skip)) … } • (done rem(INC) ) } €
NOT a total correctness logic! • We ensure low-level preservestermination of high-level • For loops: ensure progress before consuming all tokens • Notensure termination of low-level/high-level code • For loops: not ensure termination before consuming all tokens Example – loop a counter inc_loop(){ localtmp; while (true) { tmp = cnt; cas(cnt, tmp, tmp+1); } } INC_LOOP(){ while (true) { <CNT++>; } } • ⊑
INC_LOOP(){ while (true) { INC; } } • inc_loop(){ • 1 localtmp; • 2 while (true) { • 3tmp = cnt; • 4cas(cnt, tmp, tmp+1); • 5 } • } { cnt = CNT rem(INC_LOOP) } Need one token only { cnt = CNT rem(INC_LOOP)} env’s progress – reset tokens { cnt = CNT rem(INC_LOOP) (cnt = tmp cnt≠ tmp )} € if cas successful } € € { cnt = CNT+1 rem(INC_LOOP) … } rem(INC; INC_LOOP) …} execute hi-level code reset tokens since the thrd progresses { cnt = CNT rem(INC_LOOP) … } pay for the next iteration
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 envprogresses • Could reset tokens when current thread progresses
Our Logic for Verifying T ⊑ S • Judgment • R, G: rely/guarantee to describe envinterference R, G {p rem(S) } T{q rem(skip)} … 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)} • If we can find R, Gand q such that R, G {p} (T, S){q} R, G {p} (T, S){q} then we have: T⊑p S
Compositionality Rules Just like standard Rely/Guarantee rules, e.g. R1, G1 {p1} (T1,S1) {q1} G2 R1 R2, G2 {p2} (T2,S2) {q2} G1 R2 R1R2, G1G2 {p1p2} (T1ǁT2,S1ǁS2){q1q2} R, G {p} (T1,S1) {r} R, G {r} (T2,S2) {q} R, G {p} (T1;T2,S1;S2) {q}
Soundness Theorem • If we can find R, Gand q such that R, G {p} (T, S){q} then we have: T⊑p S
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(){ <CNT++> } inc(){ lock(l); cnt++; unlock(l); } inc|| inc may notterminate in an unfair execution
Hoffmann et al.’s logic for lock-freedom [LICS’13] • Not prove termination-preservation • Need to know the number of threads • Token transfer failed failed succeeded € € € € € € € € € loop: pay one token for one iteration 1 0 cnt cas: token transfer