Concurrent programming for dummies (and smart people too) Harris & Keir Fraser
Feb 25, 2016
Concurrent programming for dummies (and smart people too)
Tim Harris & Keir Fraser
Example: hashtable
Hashtable object
Array of bucketsChains of key,value pairs
Where should the locking be done?
17 13 11 5 3 27{ int result; if (!this.full) wait(); result = this.val; this.full = false; notify(); return result;}
{ if (this.full) wait(); this.full = true; this.val = val; notify();}
Example: single-cell buffer
void put(int val) int get()
Methods should be marked as synchronized ‘wait()’ can wake up spuriously so must be in a loop ‘notifyAll()’ should be used in place of ‘notify()’ for liveness
Conditional critical regions in Java
void put(int val){ atomic (!this.full) { this.full = true; this.val = val; }}
int get(){ int result; atomic (this.full) { this.full = false; return this.val; }}
Basic syntax: ‘atomic (cond) { statements; }’ Execute the statements exactly once… …starting in a state where the condition is true The statements can access fields & local variables, invoke
methods, instantiate objects etc.
Implementation overview
Source code
Bytecode + extended attributes
Software transactional memory operations
Machine code instructions
Implementation overview (ii) Native STM interface:
Transaction management void STMStartTransaction(void) boolean STMCommitTransaction(void) void STMAbortTransaction(void)
Blocking void STMWait(void)
Data access word_t STMReadValue(addr_t a) void STMWriteValue(addr_t a, word_t w)
Exposed as staticmethods
Called from interpreter / JIT’d code
Datastorage100
200a5:
a1:
Ownershiprecords (orecs)
version 42
version 17
Proposedupdates
Status: ACTIVEa1: (100,42) -> (777,43)a5: (200,17) -> (888,18)
Heap structure
Acquire exclusive access to each ownership record needed Check that they hold the correct versions Set status to committed/aborted Make updates to the heap (if needed) Release ownership records, updating the versions100
200a5:
a1: version 42
version 17
Status: ACTIVEa1: (100,42) -> (777,43)a5: (200,17) -> (888,18)
t1:CAS: 42 → t1
CAS: 17 → t1
CAS: active → committed
Status: COMMITTED777
888
CAS: t1 → 43
CAS: t1 → 18
version 43
version 18
Non-contended updates
Simple option: Spin waiting for the owner to make its updates and
release Obstruction-free option:
Make updates on owner’s behalf and then releases ownership
Intricate: first thread may make updates at a later stage. Introduces ‘active updaters’ count into each orec – details in the paper
Hacky option: Suspend the current owning thread Make their updates Revoke their ownership Change their PC to be outside the commit operation Resume the thread
Contended updates
Compound swaps
0
1
2
3
4
5
6
7
8
9
10
1 2 3 4
atomic {…}util.concurrentjava.util
#CPUs (1 thread per CPU)
μs p
er o
pera
tion
27 3717
0
200
400
600
800
1000
1200
1400
0 5 10 15 20 25 30 35 40 45 50
Compound swaps (ii)
#CPUs (1 thread per CPU)
μs p
er o
pera
tion atomic {…}
util.concurrentjava.util
Memory management
a5:
a1:
Two problems: management of transaction descriptors & management of shared data structures reachable from them
So-called ‘A-B-A’ problems occur in most CAS-based systems
We’ve looked at a number of schemes: Safe memory re-use (Michael) Repeat offender problem (Herlihy et al) Reference counting Epoch-based schemes
In many cases we’re really allocating fresh pointers rather than needing ‘more’ memory
Memory management (II)
a5:
a1:
Our more recent STM introduces a Hold/Release abstraction: A Hold operation acquires a revocable lock on a
specified location A Release operation relinquishes such a lock A lock is revoked by a competing hold or by another
thread writing to a held location Revocation is exposed by displacing the previous
owner to a specified PC This lets us ensure only one thread at a time is
working on a given transaction descriptor – MM much simplified
Software implementation using mprotect or /proc
Evaluation beyond synthetic benchmarks Try the C STM interface yourself:
download under a BSD-style license from http://www.cl.cam.ac.uk/netos/lock-free
Reflective exposure of a transactional API Create, enter, leave transactions Possibly enables better I/O handling
Opportunities for new hardware instructions
Future directions