A Calculus of Atomic Actions
Tayfun Elmas, Shaz Qadeer and Serdar Tasiran
POPL ‘09
236825 – Seminar in Distributed Algorithms
Cynthia Disenfeld
27/05/2013
*Some slides are based on those of the authors
Goal
Statically verifying (partial) correctness of
shared-memory multithreaded programs
Difficult: understand thread-interaction +
shared memory
Single-thread programs: pre/post conditions,
loop invariants
Multithreaded programs: consider the effect
of all thread-interleavings (e.g. Owicki-Gries)
Approach
(Sound) program transformations
◦ Abstraction
◦ Reduction
Invariant strengthening
Outline
Motivating examples
Approach – Soundness
Reduction
Abstraction
Borrowing assertions
Tactics
Experience / Conclusions
Motivating examples
Lock-based atomic increment
void inc() {
int t;
acquire(lock);
t := x;
t := t+1;
x := t;
release(lock);
}
void inc() {
int t;
[havoc t;
x := x+1;]
}
Motivating examples
Lock-free atomic increment
void inc() {
void inc() {
int t;
int t;
while(true){
while(*){
t := x;
t := x;
if (CAS(x,t,t+1)
assume x!=t;
break;
}
}
t := x;
}
[assume x==t;
x := t+1];
}
* Transformation from Flanagan et al.[2005]
Motivating examples
Lock-free atomic increment
void inc() {
}
void inc() {
int t;
int t;
while(*){
while (*) {
t := x;
havoc t;
assume x!=t;
skip;
}
}
t := x;
havoc t;
[assume x==t;
[assume x==t;
x := t+1];
x := t+1];
}
Motivating examples
Lock-free atomic increment
void inc() {
void inc() {
int t;
void inc() {
int t;
int t;
havoc t;
[havoc t;
while (*) {
havoc t;
skip;
x := x+1];
}
havoc t;
havoc t;
[assume x==t;
[assume x==t;
x := t+1];
}
x := t+1];
}
}
Motivating examples
Client of inc
void add(int n){
void add(int n){
while (0<n) {
while (0<n){
[assert 0<=n;
inc()
[x := x+1]
x := x+n;
n := n-1;
n := n-1;
n := 0];
}
}
void add(int n){
}
}
}
Outline
Motivating examples
Approach – Soundness
Reduction
Abstraction
Borrowing assertions
Tactics
Experience / Conclusions
QED approach
P1, I1
Proof step
P2 , I2
Program
Fine-grained concurrency
Hard to prove!
Invariant = True
If the original program may fail
...
Pn , I n
Invariant
Coarse-grained concurrency
Easy to prove
the new program may fail
Soundness
I ├ P1
P2
For all s1 ├ I
If
P1
P2
s1 error then exists s1 error
P1
If
P2
s1 s2 then exists s1 s2
or
P2
s1 error
Soundness
For each proof step
P1, I1
I 2 ├ P1
P2 , I2
P2
Proof steps:
•Invariant strengthening
•Reduction: build more coarse-grained atomic blocks
•Abstraction: add (possibly failing) behaviors
Outline
Motivating examples
Approach – Soundness
Reduction
Abstraction
Borrowing assertions
Tactics
Experience / Conclusions
Reduction
inc() {
main() {
int t;
x := 0;
acquire(lock);
inc(); || inc();
t := x;
t := t+1;
x := t;
release(lock);
}
assert(x==2);
}
Right Mover
Reduction
inc() {
int t;
R
acquire(lock);
t := x;
B
t := x;
t := t+1;
B
t := t+1;
x := t;
B
x := t;
release(lock);
inc() {
int t;
acquire(lock);
L
}
release(lock);
}
inc()
REDUCE-SEQUENTIAL
x := x+1
Reduction
main() {
x := 0;
inc(); || inc();
assert(x==2);
}
INLINE-CALL
main() {
x := 0;
B x := x+1; || x := x+1; B
assert(x==2);
}
REDUCE-PARALLEL
main() {
x := 0;
x := x+1;
x := x+1;
assert(x==2);
}
Static mover check
I ├ ;
;
Check using the current invariant if they access
different variables, or are not enabled at the
same time
Each statement consists of:
when can it be applied?
how is the state changed?
Outline
Motivating examples
Approach – Soundness
Reduction
Abstraction
Borrowing assertions
Tactics
Experience / Conclusions
Abstraction rule
Replace
with if
I ├
For all s1 ├ I
If
s1 error then exists s1 error
If
s1 s2 then exists s1 s2
or
s1 error
Abstraction
void inc() {
void inc() {
int t;
while(*){
int t;
while (*) {
SIMULATE
t := x;
havoc t;
assume x!=t;
skip;
}
}
t := x;
havoc t;
[assume x==t;
[assume x==t;
x := t+1];
x := t+1];
}
Then, we can reduce
• havoc t + skip
• while (*){…} havoc t
}
Abstraction
Adding non-determinism
◦ Guards if(*)
◦ t := x havoc t
◦ assume … skip
Adding behaviors that may go wrong
◦ x := x+1 if (x==0) fail; x := x+1
◦ y := y-x assert (y>x); y := y-x
Outline
Motivating examples
Approach – Soundness
Reduction
Abstraction
Borrowing assertions
Tactics
Experience / Conclusions
Example – Sorted linked list
Hand-over-hand locking
Find, insert, delete
Local assertions
Class invariant
Atomic easy!
But… implementation with fine-grained
locking
Insert(5)
Insert(x)
p := find(x); //locks p
n := p.next;
t := new Node();
t.val := x;
t.next := n;
p.next := t;
assert (p, t, n sorted); //they are linked as they
should and their values
have increasing order
UNLOCK(p);
Insert(x)
invariant: list is sorted
p.val and p.next are not
affected by other threads
p := find(x);
n := p.next;
t := new Node();
t.val := x;
t.next := n;
p.next := t;
assert (p, t, n sorted);
UNLOCK(p);
t.val and t.next are not
affected by other threads
Proof
P1, I1
p := find(x);
n := p.next;
t := new Node();
t.val := x;
t.next := n;
p.next := t;
assert (p, t, n
sorted);
UNLOCK(p);
P2 , I2
...
Pn , I n
find appropriate p
LOCK(p)
n := p.next;
t := new Node();
t.val := x;
t.next := n;
R
p.next := t;
assert (p, t, n sorted);
UNLOCK(p);
L
Proof
P1, I1
P2 , I2
find appropriate p
LOCK(p)
n := p.next;
t := new Node();
t.val := x;
t.next := n;
...
Pn , I n
find appropriate p
LOCK(p)
n := p.next;
R
t := new Node();
t.val := x;
t.next := n;
p.next := t;
assert (p, t, n sorted);
p.next := t;
assert (p, t, n sorted);
UNLOCK(p);
UNLOCK(p);
how to continue?
L
Apparent interference
Thread A
Thread B
p.next := t;
n := p.next;
n := p.next;
p.next := t;
Apparent interference
Thread A
Thread B
p.next := t;
n := p.next;
n := p.next;
p.next := t;
But: both p’s are locked!
Ruling out interference - 1
Thread A
Thread B
assert owner[p]==A
p.next := t;
assert owner[p]==B
n := p.next;
assert owner[p]==B
n := p.next;
assert owner[p]==A
p.next := t;
Ruling out interference - 2
Thread A
Thread B
assert !inList[t]
t.next := n;
assert inList[p]
n := p.next;
assert inList[p]
n := p.next;
assert !inList[t]
t.next := n;
Reduction after abstraction
find appropriate p
LOCK(p)
find appropriate p
LOCK(p)
assert inList[p]&&owner[p]==tid
n := p.next;
t := new Node();
t.val := x;
assert !inList[t]
t.next := n;
assert inList[p]&&owner[p]==tid
p.next := t;
assert (p, t, n sorted);
assert owner[p]==tid
UNLOCK(p);
assert inList[p]&&owner[p]==tid
n := p.next;
t := new Node();
t.val := x;
assert !inList[t]
t.next := n;
assert inList[p]&&owner[p]==tid
p.next := t;
assert (p, t, n sorted);
assert owner[p]==tid
UNLOCK(p);
Borrowed assertions
find appropriate p
LOCK(p)
find appropriate p
LOCK(p)
assert inList[p]&&owner[p]==tid
n := p.next;
t := new Node();
t.val := x;
assert !inList[t]
t.next := n;
assert inList[p]&&owner[p]==tid
p.next := t;
assert (p, t, n sorted);
assert owner[p]==tid
UNLOCK(p);
assert inList[p]&&owner[p]==tid
n := p.next;
t := new Node();
t.val := x;
assert !inList[t]
t.next := n;
assert inList[p]&&owner[p]==tid
p.next := t;
assert (p, t, n sorted);
assert owner[p]==tid
UNLOCK(p);
Completing the proof
Invariant : List is sorted
find appropriate p
LOCK(p)
n := p.next;
t := new Node();
t.val := x;
t.next := n;
p.next := t;
assert (p, t, n sorted);
UNLOCK(p);
Outline
Motivating examples
Approach – Soundness
Reduction
Abstraction
Borrowing assertions
Tactics
Experience / Conclusions
Tactics
High-level strategies multiple rule
proofs
◦ abstract from a read, write
◦ add invariants
◦ synchronization mechanisms
Example
mutex P, x1, … , xn
mutex (lock==true), x
inc() {
int t;
acquire(lock);
t := x;
t := t+1;
x := t;
release(lock);
}
acquire(lock) {
assume lock==false;
lock := true;
}
release(lock) {
lock := false;
}
Tactic - mutex
inc() {
int t;
acquire(lock); a=tid;
t := x;
t := t+1;
x := t;
release(lock); a=0;
}
Invariant: lock==true iff a !=0
AUX-ANNOTATE
Tactic - mutex
inc() {
inc() {
int t;
int t;
acquire(lock); a=tid;
acquire(lock); a=tid;
t := x;
assert a==tid;t := x;
t := t+1;
t := t+1;
x := t;
assert a==tid;x := t;
release(lock); a=0;
assert a==tid;
}
release(lock); a=0;
}
SIMULATE
Tactic - mutex
inc() {
int t;
acquire(lock); a=tid;
assert a==tid;t := x;
t := t+1;
assert a==tid;x := t;
assert a==tid;
release(lock); a=0;
}
R
B
B
B
L
Tactic - mutex
inc() {
int t;
acquire(lock); a=tid;
assert a==tid;
t := x;
t := t+1;
assert a==tid;
x := t;
assert a==tid;
release(lock); a=0;
}
REDUCE & RELAX
Outline
Motivating examples
Approach – Soundness
Reduction
Abstraction
Borrowing assertions
Tactics
Experience / Conclusions
Experience
Implementation
◦ Boogie + parallel composition
◦ Verification conditions for validity of each
step: Z3 SMT Solver
Benchmarks without complicated
global invariants
Fine-grained locking
◦ Multiset
◦ Hand-over-hand locking
Non-blocking algorithms
Future work
More tactics
More synchronization mechanisms
C / Spec#
Larger verification problems
Conclusions
A formal and sound proof calculus for
atomicity was presented
Abstraction helps applying reduction and
the other way around
Assertions can be added and checked
only later
The program is simplified by obtaining
coarser atomic actions
Tactics can be defined to represent
different synchronization mechanisms
© Copyright 2026 Paperzz