a program logic for concurrent objects under fair scheduling hongjin liang and xinyu feng university...
TRANSCRIPT
A Program Logic forConcurrent Objects
under Fair Scheduling
Hongjin Liang and Xinyu FengUniversity of Science and Technology of China (USTC)
To appear at POPL 2016
…push(7);x = pop();…
…push(6);…
Client code C
java.util.concurrent
void push(int v) { … }
…
int pop() { … }
Concurrent Object O
Whole program C[O]
Correctness of O
• 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 synchronization- Program Logics: Gotsman et al. POPL’09, Hoffmann et al. LICS’13, …
Blocking synchronization- Program Logics: ???
DF and SF as Progress Properties• DF: in each fair execution, there always exists some
method call that can finish• Fair sched: every active thread gets eventually executed
• Disallow non-termination of critical section
f() {
lock L;
while (true) {};
unlock L;
}
[Herlihy and Shavit 2011]
DF and SF as Progress Properties• DF: in each fair execution, there always exists some
method call that can finish• Fair sched: every active thread gets eventually executed
• Disallow non-termination of critical section
• Disallow live-lockg1() { lock L1; while (!available(L2)) { unlock L1; lock L1; } unlock L1;}
g2() { lock L2; while (!available(L1)) { unlock L2; lock L2; } unlock L2;}
[Herlihy and Shavit 2011]
DF and SF as Progress Properties• DF: in each fair execution, there always exists some
method call that can finish• Fair sched: every active thread gets eventually executed
• Disallow non-termination of critical section
• Disallow live-lock
• Possible ad-hoc synchronization
h1() { x := 1; while (y != 0) {}; x := 0;}h2() { y := 1; while (x != 0) {}; y := 0;}
[Herlihy and Shavit 2011]
DF and SF as Progress Properties• DF: in each fair execution, there always exists some
method call that can finish• Disallow non-termination of critical section• Disallow live-lock• Possible ad-hoc synchronization
Not verified in existing work which views DF as a safety property
• SF: in each fair execution, every method call can finish
[Herlihy and Shavit 2011]
Our Contributions
• Program Logic LiLi for Linearizability & Liveness
• Identify two challenges in progress verification, addressed by separate mechanisms
• Unify thread-local reasoning about DF and SF• One set of inference rules (also applied to verifying LF and
WF)
• Examples: • Ticket locks, queue locks, TAS locks, two-lock queues, …
• We’re the first to verify SF of lock-coupling lists and DF of optimistic lists and lazy lists
Challenges in Progress Verification• In sequential settings, no infinite loops termination
• In concurrent settings, env interference may affect termination of a method• Blocking
• Delay
Challenge 1: Blocking
• Caused by absence of env’s certain actions (e.g. unlock)
f() {
lock L;
unlock L;
}
g() {
lock L;
}
f(); || g();
f may never terminate because it keeps waiting for env’s unlock action
Not acceptable.
Challenge 1: Blocking
• Caused by absence of env’s certain actions (e.g. unlock)
• Sometimes bad: the certain action never happens
• Sometimes acceptable: the certain action eventually happens under fair scheduling
• SF and DF assume fair scheduling
f() {
lock L;
unlock L;
}
f(); || f();
Left f may wait for env’s unlock.But it will terminate under fair scheduling.
Challenge 1: Blocking
• Caused by absence of env’s certain actions (e.g. unlock)
• Sometimes bad: the certain action never happens
• Sometimes acceptable: the certain action eventually happens under fair scheduling
How to support “acceptable” blocking but prevent “bad” blocking?
Our idea: introduce definite actions D to specify the certain actions that eventually happen
Our idea: definite actions D
• D is a special action in the form of P Q
• Enabled(D) is defined as P
• D should be “definite”: Q should eventually be reached (regardless of env) once P holds
the thread has released lock
the thread has acquired lock
D
Enabled
assume fair scheduling
P
Q
Queue management in banks
Using D to verify counter with ticket lock
inc() { local i, r; i := getAndInc( next ); while( i != owner ) {} ; // acquire lock r := cnt; cnt := r + 1; // critical section owner := i + 1; // release lock}
the next available ticketcurrently being served
nextowneri
Using D to verify counter with ticket lock
inc() { local i, r; i := getAndInc( next ); while( i != owner ) {} ; // acquire lock r := cnt; cnt := r + 1; // critical section owner := i + 1; // release lock}
0 1 2 3
T1: request lock
T2: request lock
T1: release lock
T3: request lock
owner
next
Lock acquired
It’s SF, because• critical section is straight-line code • threads requesting the lock form a queue
Lock acquired
Using D to verify counter with ticket lock
T is blocked: it is waiting for env threads ahead of it in the queue to finish D next
owner
0 1 2 3
TT2T1T is blocked
Lock acquired
the thread has released lock
the thread acquires lock
D How to establish D (prove D is indeed “definite”) ?Prove critical section terminates no matter what env does
T will get lockT2 releases lock
T2 gets lock
D
T1 releases lock
T1 gets lock
DEnabledinitially
next
owner
0 1 2 3
TT2T1
DProgress: T will be unblocked after env finishes a finite number of Ds
Enable
fair sched T is blockedLock acquired
which form a queue to ensure SF
Queue length is a decreasing metric
Summary of the definite action idea Support “good” blocking
• D will definitely happen under fair sched, regardless of env
• DProgress: any blocked thread will be unblocked after env finishes a finite queue of Ds
SF: every method can finish in any fair execution
More examples: queue locks, lock-coupling lists
DF: blocking and delay are intertwined
Challenge 2: Delay
• Caused by occurrence of env’s certain actions (e.g. object state updates via cas)
incDF() {
local b, r;
b := false;
while( !b ) {
b := cas(&L, 0, T);
}
r := cnt; cnt := r + 1; // critical section
L := 0;
}
current thread ID
Example: counter with cas lock
incDF(); while(true) incDF();
Left incDF may never terminate due to env’s infinite num of successful cases.
Acceptable, since DF requires only whole-system progress.
Delay!
D & DProgress idea for blocking does not allow delay in the DF counter
T will get lockT1 releases lock
T1 is holding lock
DT2 gets lock
?T is blocked
Queue jumps!
DProgress: T will be unblocked after env finishes a finite queue of Ds, where queue length is a decreasing metricQueue jumps delay the progress of the unblocked T.
Allowed, since DF requires only whole-system progress.
Relax DProgress to allow queue jumps
DProgress: T will be unblocked after env finishes a finite queue of Ds, where queue length can be increased when delayed by env.
T will get lockT1 releases lock
T1 is holding lock T2 gets lock
?
D Queue jumps!
Problem: How to prevent infinite delays without whole-system progress?
Tokens ensure no infinite delays before whole-system progress
• Our idea: assign tokens• Consume a token for a delaying action
• Similar ideas have been used to verify LF (i.e. whole-system progress under any scheduling)
[Hoffmann et al LICS’13, Liang et al CSL-LICS’14]
Tokens ensure no infinite delays before whole-system progress
inc() {
{ p * } local b, r; b := false; while( !b ) { b := cas(&L, 0, T); } r := cnt; cnt := r + 1; L := 0;
{ q }}
pay at successful cas (the only action that can jump other threads’ queues)
Summary for DF verification
• Blocking and delay are intertwined
• Blocking: • D will definitely happen under fair sched, regardless of env• DProgress: a blocked thread waits for env to finish a finite queue of Ds• Allow queue jumps (queue length increases) when delayed by env
• Delay:• Each method is assigned a finite number of tokens• Delaying action: pay one token
DF: whole system progress under fair scheduling
By turning on/off mechanisms for blocking & delay, we can support all the four progress properties.
non-delay delaynon-blocking wait-freedom lock-freedom
blocking starvation-freedom deadlock-freedom
Trickier: Blocking + Delay + Rollback
add(e) { // all locks are CAS locks local b := false, p, c; while (!b) { (p, c) := find(e); lock p; lock c; b := validate(p, c); if (!b) { unlock c; unlock p; } } … // insert e between p and c unlock c; unlock p;}
Examples: optimistic lists and lazy lists
It’s deadlock-free.
Problem:A thread may lock a node for an unbounded num of times. Need infinite tokens?
Our solution: stratify tokens (see the paper)
Our logic LiLi
• Judgment
• O : concrete method impl (e.g. cas-lock counter)
• A : abstract atomic spec (e.g. <cnt++>)
• D : definite actions
• R, G : rely/guarantee to describe env interference (also label delaying actions)
• p : concrete & abstract states & tokens
D, R, G { p } O : A
Soundness Theorem for LiLi
a) O is linearizable w.r.t. A
If , then we have:{p} O : AD, R, G
b) O is deadlock-free
c) if R & G do not have delaying actions, then O is starvation-free
d) if D is not used, then O is lock-free
e) if D is not used and R & G do not have delaying actions, then O is wait-free
non-delay delay
non-blocking
WF LF
blocking SF DF
Summary: LiLi for Linearzability & Liveness• Blocking: definite actions
• Delay: tokens
• Unified: By ignoring either or both features, LiLi can be instantiated to verify all the four progress properties
non-delay delaynon-blocking wait-freedom lock-freedom
blocking starvation-freedom deadlock-freedom
Backup Slides
Obstruction-Freedom
• Progress when the thread executes in isolation (without interference from env)
non-delay “good” delay
unlimited delay
non-blocking
wait-freedom
lock-freedom
obstruction-freedom
“good”
blockingstarvation-freedom
deadlock-freedom
Obstruction-Freedom
• Progress when the thread executes in isolation (without interference from env)
g1() { while (x > 0) { x--; }}
g2() { while (x < 10) { x++; }}
Comparisons with earlier token-based work for LF verification
• We all assign -tokens to loops (pay at each round)
• But LiLi also assigns -tokens for delaying actions
• Earlier work assumes each method has only one delaying action, which is at the linearization point (LP)
[Hoffmann et al LICS’13, Liang et al CSL-LICS’14]
incLF(){
local done, r;
done := false;
while (!done) {
r := cnt;
done := cas(cnt, r, r + 1); // LP
}
}
Stratify tokens and delaying actions
add(e) { { (1, 2) … } local b := false, p, c; while (!b) { (p, c) := find(e); { valid(p, c) (1, 2) … … } lock p; lock c; { valid(p, c) (1, 0) … invalid(p, c) (1, 2) … } b := validate(p, c); if (!b) { unlock c; unlock p; } } … // insert e between p and c unlock c; unlock p;}
invalid(p, c) (1, 4) … }
Could reset level-1 tokens when delayed by env’s level-2 action
Level 2: for add/removeLevel 1: for lock p & lock c
Prevent Live-Lock by stratification of -tokens & delaying actions
• Level 2: lock L2
• Level 1: lock L1
• When g2 locks L2, g1 gets more 1-level -tokens
• But 2-level -tokens do not increase!
g1() { lock L1; while (available(L2)) { unlock L1; lock L1; } unlock L1;}
g2() { lock L2; while (available(L1)) { unlock L2; lock L2; } unlock L2;}
g1(); || g2();
Establish D
• Termination of loop (“critical section”) when D is enabled
• p’ has one less -token than p• one token is consumed to start the new iteration
• -tokens do not increase, unless delayed by env
{p’} C {p}
D, R, G
{p} while B do C {p B}D, R, G
p B Enabled(D) p’ * …
Establish D• Termination of loop (“critical section”) when D is
enabled
• Global constraints (at TOP rule)
{p’} C {p}
D, R, G
{p} while B do C {p B}D, R, G
p B Enabled(D) p’ * …
{p arem(A) (E)} C {p arem(skip)}D, R, G
{p} C : AD, R, G
p Enabled(D) G D (Enabled(D) Enabled(D)) …
DProgress queue for blocking
{p’} C {p}
D, R, G
{p} while B do C {p B}D, R, G
p B (Enabled(D) Q) p’ *
p DProgress(n, D, Q) DProgress(n, D, Q) stable
The full rule for while
{p’} C {p}
D, R, G
{p} while B do C {p B}D, R, G
p B (Enabled(D) Q) p’ *
p DProgress(n, D, Q) DProgress(n, D, Q) stable
Tokens for delay
• ATOM rule: Consume -tokens
• q’ k q : k-level -tokens decrease
• Stable(p, R)• Reset j-level -tokens for k-level env actions where j < k• Reset -tokens to loop more rounds
{p} C {q’}
SL
{p} atomic{C} {q}D, R, G
q’ k q (p k q) G
Why linearizability and progress together?
• Progress-aware abstractions for concurrent objects
Contextual refinement O AP
Linearizability O lin A Progress P(O)
D, R, G { p } O : A
Progress-aware spec
Progress-aware specs ASF and ADF
• O AP: • Assume fair scheduling
• Preserve termination behaviors
• ASF : atomic spec A
• ADF : wrap A with delaying codeO AP
O lin A P(O)
D, R, G { p } O : A
DF-aware specO wr(A)
O lin A DF(O)D, R, G { p } O : A