Verifying Transactional Programs
with
Programmer-Defined Conflict Detection
Omer Subasi, Serdar Tasiran (Koç University)
Tim Harris (Microsoft Research)
Adrian Cristal, Osman Unsal (Barcelona SC)
Ruben Titos-Gil (University of Murcia)
Motivation: Linked List from Genome
Insert 5
5
Head
1
3
6
9
12
15
17
16
Insert 16
Head
1
3
6
READ
9
12
15
17
5
WRITE
Head
1
3
6
READ
9
12
15
17
WriteAfterRead
conflict!
5
WRITE
Head
1
3
6
READ
9
12
15
WRITE
16
17
WriteAfterRead
conflict!
5
WRITE
Head
1
3
6
9
12
READ
15
WRITE
16
• Conventional TM conflict detection
– Insertions conflict often
17
5
Head
1
3
6
9
12
15
17
16
• But, both insertions OK even if we ignore WAR conflict
⇒ Relaxed conflict detection
User-Defined Conflict Detection
• Ignore write-after-read conflicts (Titos et al):
atomic[!WAR]{
insert method body
}
8
Linked List: Insert
list_insert(list_t *listPtr, node_t *node) {
atomic {
*curr = listPtr->head;
do {
prev = curr;
curr = curr->next;
} while (curr != NULL &&
curr->key < node->key);
node->next = curr;
prev->next = node;
}
}
9
Linked List: Insert
list_insert(list_t *listPtr, node_t *node) {
atomic {
*curr = listPtr->head;
do {
prev = curr;
curr = curr->next;
} while (curr != NULL &&
curr->key < node->key);
Strict conflict
detection:
Can reason about
transaction code
sequentially.
node->next = curr;
prev->next = node;
assert(node is in the list && list is sorted);
}
}
10
Linked List: Insert
list_insert(list_t *listPtr, node_t *node) {
atomic [!WAR]{
Ignore Write-After-Read
conflicts:
*curr = listPtr->head;
do {
prev = curr;
curr = curr->next;
} while (curr != NULL &&
curr->key < node->key);
• Writes by others
can occur between
read phase and write
phase
• Lost ability to reason
sequentially
node->next = curr;
prev->next = node;
assert(node is in the list && list is sorted);
5
WRITE
}
}
1
3
6
READ
11
Making !WAR block atomic
list_insert(list_t *listPtr, node_t *node) {
atomic [!WAR]{
*curr = listPtr->head;
do {
prev = curr;
curr = curr->next;
} while (curr != NULL &&
curr->key < node->key);
Would like this action to
be “right mover”
• Can commute to the
right of any action
by another thread
node->next = curr;
prev->next = node;
assert(node is in the list && list is sorted);
}
}
12
Right Mover
commutes to the right of if
; goes to state S then
; goes to same state S.
If is right-mover:
;
;
13
Making !WAR block atomic
list_insert(list_t *listPtr, node_t *node) {
atomic [!WAR]{
Ignored WAR conflict:
currT1 = currT1->next;
*curr = listPtr->head;
do {
prev = curr;
currT1 = currT1->next;
} while (curr != NULL &&
curr->key < node->key);
node->next = curr;
prev->next = node;
assert(node is in the list && list is sorted);
does not move to the
right of
nodeT2->next = curr;
nodeT2->next = curr;
Solution:
Abstraction
}
}
14
Want to
read 6
again!
Abstraction intuition
READ 6; WRITE 5
1
3
ABSTRACT READ 6;
WRITE 5
1
6
5
3
5
WRITE 5; READ 5
5
ABSTRACT READ:
Read anything
forward but do
not pass the key.
6
1
3
WRITE 5;
ABSTRACT READ 6
1
3
6
Need to
jump over
5.
5
6
15
Want to
read 6
again!
Abstraction intuition
READ 6; WRITE 5
1
curr
3 = curr->next;
6
with
curr = curr->next*;
but don’t go past key.
ABSTRACT READ 6;
WRITE 5
1
1
3
WRITE 5;
ABSTRACT READ 6
5
3
5
WRITE 5; READ 5
5
6
1
3
6
Need to
jump over
5.
5
6
16
Abstraction
list_insert(list_t *listPtr, node_t *node) {
atomic [!WAR]{
*curr = listPtr->head;
do {
prev = curr;
currT1 = currT1->next*;
} while (curr != NULL &&
curr->key < node->key);
}
}
Solution: Abstraction
Replace
currT1 = currT1->next;
with
currT1 = currT1->next*;
but don’t go past key.
• Now the block is
atomic.
• Can do sequential
node->next = curr;
verification.
prev->next = node;
assert(node is in the list && list is sorted); • Use a sequential
verification tool such as
HAVOC, VCC…
17
1. Sequentially prove the original code
list_insert(list_t *listPtr, node_t *node) {
*curr = listPtr->head;
do {
prev = curr;
curr = curr->next;
} while (curr != NULL &&
curr->key < node->key);
node->next = curr;
prev->next = node;
assert(node is in the list && list is sorted);
}
}
18
2. Apply the program transformation
list_insert(list_t *listPtr, node_t *node) {
*curr = listPtr->head;
do {
prev = curr;
curr = curr->next*;
} while (curr != NULL &&
curr->key < node->key);
Do global read abstractions
Abstract transaction
becomes atomic .
node->next = curr;
prev->next = node;
assert(node is in the list && list is sorted);
}
}
19
3. Prove sequentially properties on
abstract code
list_insert(list_t *listPtr, node_t *node) {
*curr = listPtr->head;
do {
prev = curr;
curr = curr->next*;
} while (curr != NULL &&
curr->key < node->key);
node->next = curr;
prev->next = node;
assert(node is in the list && list is sorted);
}
}
Finally: Soundness theorem says properties hold
on original code, with !WAR ignored.
20
Other proofs
• Labyrinth from STAMP.
• StringBuffer pool.
• Can apply to any program that has the pattern
where:
• a large portion of the shared data is read first
• local computations is done and then
• a small portion of shared data is updated
21
© Copyright 2026 Paperzz