Professional Documents
Culture Documents
Concurrent Objects: Companion Slides For by Maurice Herlihy & Nir Shavit
Concurrent Objects: Companion Slides For by Maurice Herlihy & Nir Shavit
Concurrent Objects: Companion Slides For by Maurice Herlihy & Nir Shavit
Companion slides for The Art of Multiprocessor Programming by Maurice Herlihy & Nir Shavit
Concurrent Computation
memory
object
Art of Multiprocessor Programming
object
2
Objectivism
What is a concurrent object?
How do we describe one? How do we implement one? How do we tell if were right?
Objectivism
What is a concurrent object?
How do we describe one?
How do we tell if were right?
q.enq( )
q.deq()/
A Lock-Based Queue
class LockBasedQueue<T> { int head, tail; T[] items; Lock lock; public LockBasedQueue(int capacity) { head = 0; tail = 0; lock = new ReentrantLock(); items = (T[]) new Object[capacity]; }
A Lock-Based Queue
head
0 capacity-1
tail
1
y z 2 class LockBasedQueue<T> { int head, tail; T[] items; Lock lock; public LockBasedQueue(int capacity) { head = 0; tail = 0; lock = new ReentrantLock(); items = (T[]) new Object[capacity]; }
A Lock-Based Queue
head
0 capacity-1
tail
1
y z 2 class LockBasedQueue<T> { int head, tail; T[] items; Lock lock; public LockBasedQueue(int capacity) { head = 0; tail = 0; lock = new ReentrantLock(); items = (T[]) new Object[capacity]; }
Implementation: Deq
head tail
0 1 2
public T deq() throws EmptyException capacity-1 { y z lock.lock(); try { if (tail == head) throw new EmptyException(); T x = items[head % items.length]; head++; return x; } finally { lock.unlock(); } }
10
Implementation: Deq
head
0 1
tail
public T deq() throws EmptyException capacity-1 { y z 2 lock.lock(); try { if (tail == head) throw new EmptyException(); T x = items[head % items.length]; head++; return x; } finally { Method calls lock.unlock(); mutually exclusive } }
11
Implementation: Deq
head
0 1
tail
public T deq() throws EmptyException capacity-1 { y z 2 lock.lock(); try { if (tail == head) throw new EmptyException(); T x = items[head % items.length]; head++; return x; } finally { If queue empty lock.unlock(); } throw exception }
12
Implementation: Deq
head
0 1
tail
public T deq() throws EmptyException capacity-1 { y z 2 lock.lock(); try { if (tail == head) throw new EmptyException(); T x = items[head % items.length]; head++; return x; } finally { Queue not empty: lock.unlock(); } remove item and update }
head
13
Implementation: Deq
head tail
0 1
public T deq() throws EmptyException capacity-1 { y z 2 lock.lock(); try { if (tail == head) throw new EmptyException(); T x = items[head % items.length]; head++; return x; } finally { Return result lock.unlock(); } }
14
Implementation: Deq
head
0 1
tail
public T deq() throws EmptyException capacity-1 { y z 2 lock.lock(); try { if (tail == head) throw new EmptyException(); T x = items[head % items.length]; head++; return x; Release lock no } finally { matter what! lock.unlock(); } }
15
Implementation: Deq
public T deq() throws EmptyException { lock.lock(); try { if (tail == head) throw new EmptyException(); T x = items[head % items.length]; head++; return x; } finally { lock.unlock(); } }
16
17
tail
y z
1 2
public void enq(Item x) { if (tail-head == capacity) throw new FullException(); items[tail % capacity] = x; tail++; } public Item deq() { if (tail == head) throw new EmptyException(); Item item = items[head % capacity]; head++; return item; }} Art of Multiprocessor 18
Programming
tail tail
1
yy zz
1
2 2
public void enq(Item x) { if (tail-head == capacity) throw new FullException(); items[tail % capacity] = x; tail++; } public Item deq() { if (tail == head) throw new EmptyException(); Item item = items[head % capacity]; head++; return item; }} Art of Multiprocessor 19
Programming
tail
public void enq(Item x) { if (tail-head == capacity) throw new FullException(); items[tail % capacity] = x; tail++; } public Item deq() { if (tail == head) throw new EmptyException(); Queue is updated without lock! Item item = items[head %a capacity]; head++; return item; }} Art of Multiprocessor 20
Programming
Sequential Objects
Each object has a state
Usually given by a set of fields Queue example: sequence of items
23
Sequential Specifications
If (precondition) the object is in such-and-such a state before you call the method, Then (postcondition) the method will return a particular value or throw a particular exception. and (postcondition, cont) the object will be in some other state when the method returns,
Art of Multiprocessor Programming 24
Postcondition:
Returns first item in queue
Postcondition:
Removes first item in queue
25
Postcondition:
Throws Empty exception
Postcondition:
Queue state unchanged
26
27
28
time
Art of Multiprocessor Programming 29
time
Art of Multiprocessor Programming 30
response 12:01
void Method call time
Art of Multiprocessor Programming 33
Sequential vs Concurrent
Sequential
Methods take time? Who knew?
Concurrent
Method call is not an event Method call is an interval.
34
time
Art of Multiprocessor Programming 35
Method call
time
Art of Multiprocessor Programming 36
Method call
38
Sequential vs Concurrent
Sequential:
Object needs meaningful state only between method calls
Concurrent
Because method calls overlap, object might never be between method calls
39
Sequential vs Concurrent
Sequential:
Each method described in isolation
Concurrent
40
Sequential vs Concurrent
Sequential:
Can add new methods without affecting older methods
Concurrent:
Everything can potentially interact with everything else
41
Sequential vs Concurrent
Sequential:
Can add new methods without affecting older methods
Concurrent:
Everything can potentially interact with everything else
42
43
Intuitively
public T deq() throws EmptyException { lock.lock(); try { if (tail == head) throw new EmptyException(); T x = items[head % items.length]; head++; return x; } finally { lock.unlock(); } }
44
Intuitively
public T deq() throws EmptyException { lock.lock(); try { if (tail == head) throw new EmptyException(); T x = items[head % items.length]; head++; return x; } finally { All modifications lock.unlock(); of queue are done } mutually exclusive }
45
Lets capture the idea of describing the concurrent via the sequential
lock()
Intuitively
q.deq deq
unlock()
q.enq
lock()
enq
unlock()
Behavior is Sequential
time enq
deq
46
Linearizability
Each method should
take effect Instantaneously Between invocation and response events
Object is correct if this sequential behavior is correct Any such concurrent object is
Linearizable
47
Sounds like a property of an execution A linearizable object: one all of whose possible executions are linearizable
Art of Multiprocessor Programming 48
Example
time
(6)
49
Example
q.enq(x)
time
(6)
50
Example
51
Example
52
Example
q.deq(y)
53
Example
q.deq(y)
54
Example
q.deq(y)
55
Example
time
(5)
56
Example
q.enq(x)
time
(5)
57
Example
q.enq(x)
q.deq(y)
time
(5)
58
Example
q.enq(x)
(5)
59
Example
q.enq(x)
q.deq(y)
q.enq(y)
time
(5)
60
Example
q.enq(x)
q.deq(y)
q.enq(y)
time
(5)
61
Example
time
(4)
62
Example
q.enq(x)
time
(4)
63
Example
q.enq(x)
q.deq(x) time
(4)
64
Example
q.enq(x)
q.deq(x)
time
(4)
65
Example
q.enq(x)
q.deq(x)
time
(4)
66
Example
q.enq(x)
time
(8)
67
Example
68
Example
q.enq(x) q.enq(y)
q.deq(y)
time
(8)
69
Example
q.enq(x) q.enq(y)
(8)
70
q.enq(x)
q.deq(y)
q.enq(y)
time
q.deq(x)
71
write(0)
write(2) read(0)
(4)
72
write(0)
read(1) write(1)
write(2) read(0)
73
write(0)
read(1) write(1)
write(2) read(0)
74
write(0)
read(1) write(1)
write(2) read(0)
75
write(0)
read(1) write(1)
write(2) read(1)
76
write(0)
read(1) write(1)
write(2) read(1)
77
write(0)
read(1) write(1)
write(2) read(1)
78
write(2) read(1)
79
write(2) read(1)
80
write(2) read(1)
81
write(0)
write(2) read(1)
(2)
82
write(0)
write(2) read(1)
(2)
83
write(0)
write(2) read(1)
(2)
84
write(0)
write(2) read(2)
(2)
85
Not Always
In some cases, linearization point depends on the execution
86
Allow reasoning
Formal But mostly informal
In the long run, actually more important Ask me why!
87
Invocation Notation
A q.enq(x)
(4)
89
Invocation Notation
A q.enq(x) thread
(4)
90
Invocation Notation
A q.enq(x) thread method
(4)
91
Invocation Notation
A q.enq(x) thread object
(4)
method
92
Invocation Notation
A q.enq(x) thread object
(4)
method arguments
93
Response Notation
A q: void
(2)
94
Response Notation
A q: void thread
(2)
95
Response Notation
A q: void thread result
(2)
96
Response Notation
A q: void thread object
(2)
result
97
Response Notation
A q: void thread object
(2)
result
98
Response Notation
A q: empty() thread object
(2)
exception
99
A A A H = B B B B
Definition
Invocation & response match if Thread names agree A q:void
(1)
A q.enq(3)
101
Object Projections
H=
A A B B B B
Object Projections
H|q =
A A B B B B
Thread Projections
H=
A A B B B B
Thread Projections
H|B =
A A B B B B
Complete Subhistory
A A A H = B B B B q.enq(3) q:void q.enq(5) p.enq(4) p:void q.deq() An invocation is q:3 pending if it has no matching respnse
106 Art of Multiprocessor Programming
Complete Subhistory
A A A H = B B B B q.enq(3) q:void q.enq(5) p.enq(4) p:void q.deq() May or may not q:3 have taken effect
Art of Multiprocessor Programming 107
Complete Subhistory
A A A H = B B B B q.enq(3) q:void q.enq(5) p.enq(4) p:void q.deq() q:3
Art of Multiprocessor Programming
Complete Subhistory
A q.enq(3) A q:void Complete(H) = B B B B p.enq(4) p:void q.deq() q:3
Art of Multiprocessor Programming 109
Sequential Histories
A A B B B B A
110
Sequential Histories
match
A A B B B B A
111
Sequential Histories
match
A A B B B B A
match
112
Sequential Histories
match
A A B B B B A
match
match
113
Sequential Histories
match
A A B B B B A
match
match Final pending invocation OK
Art of Multiprocessor Programming 114
Sequential Histories
match
A A B B B B A
match
match Final pending invocation OK
Art of Multiprocessor Programming 115
Well-Formed Histories
A B B H= B A B
116
Well-Formed Histories
Per-thread projections sequential A B B H= B A B q.enq(3) p.enq(4) p:void q.deq() q:void q:3
B H|B= B B B
117
Well-Formed Histories
Per-thread projections sequential A B B H= B A B q.enq(3) p.enq(4) p:void q.deq() q:void q:3
B H|B= B B B
Equivalent Histories
Threads see the same thing in both A B B H= B A B q.enq(3) p.enq(4) p:void q.deq() q:void q:3
A A B G= B B B
Art of Multiprocessor Programming
Sequential Specifications
A sequential specification is some way of telling whether a
Single-thread, single-object history Is legal
For example:
Pre and post-conditions But plenty of other techniques exist
120
Legal Histories
A sequential (multi-object) history H is legal if
For every object x H|x is in the sequential spec for x
121
Precedence
A B B A B B q.enq(3) p.enq(4) p.void q:void q.deq() q:3
(1)
Non-Precedence
A B B B A B q.enq(3) p.enq(4) p.void q.deq() q:void q:3
(1)
123
Notation
Given
History H method executions m0 and m1 in H m0 precedes m1
m0 m1
We say m0 H m1, if
Relation m0 H m1 is a
Linearizability
History H is linearizable if it can be extended to G by
Appending zero or more responses to pending invocations Discarding other pending invocations Legal sequential history S where G S
Art of Multiprocessor Programming
So that G is equivalent to
125
What is G S
G = {ac,bc} S = {ab,ac,bc}
a b G time
(8)
S
126
Remarks
Some pending invocations
Took effect, so keep them Discard the rest
Condition G S
127
Example
A B B B B B q.enq(3) q.enq(4) q:void q.deq() q:4 q:enq(6)
B. q.enq(6)
Example
A B B B B B q.enq(3) q.enq(4) q:void q.deq() q:4 q:enq(6) Complete this pending invocation
B. q.enq(6)
Example
A B B B B B A q.enq(3) q.enq(4) q:void q.deq() q:4 q:enq(6) q:void
A.q.enq(3) B.q.enq(4) B.q.deq(4) time
Art of Multiprocessor Programming 130
B. q.enq(6)
Example
A B B B B B A q.enq(3) q.enq(4) q:void q.deq() q:4 q:enq(6) q:void
A.q.enq(3) B.q.enq(4) B.q.deq(4) time
Art of Multiprocessor Programming 131
B. q.enq(6)
Example
A B B B B q.enq(3) q.enq(4) q:void q.deq() q:4
A q:void
A.q.enq(3) B.q.enq(4) B.q.deq(4) time
Art of Multiprocessor Programming 132
Example
A B B B B A q.enq(3) q.enq(4) q:void q.deq() q:4 q:void
A.q.enq(3)
B.q.enq(4) B.q.deq(4)
time
133
Example
A B B B B A q.enq(3) q.enq(4) q:void q.deq() q:4 q:void B B A A B B
A.q.enq(3)
B.q.enq(4) B.q.deq(4)
time
134
Example
Equivalent sequential history A B B B B A q.enq(3) q.enq(4) q:void q.deq() q:4 q:void B B A A B B
A.q.enq(3)
B.q.enq(4) B.q.deq(4)
time
135
Composability Theorem
History H is linearizable if and only if
For every object x H|x is linearizable
141
142
head
tail
2
143
144
tail tail
1
yy zz
1
2 2
public void enq(Item x) { if (tail-head == capacity) throw new FullException(); items[tail % capacity] = x; tail++; } public Item deq() { if (tail == head) throw new EmptyException(); Item item = items[head % capacity]; head++; return item; }} Art of Multiprocessor 145
Programming
public void enq(Item x) { if (tail-head == capacity) throw new FullException(); items[tail % capacity] = x; tail++; } public Item deq() { if (tail == head) throw new EmptyException(); Item item = items[head % capacity]; head++; return item; }} Art of Multiprocessor 146
Programming
Linearization order is y z tail = 0; order head and tail new Object[capacity]; fields modified
0 1 capacity-1 2
head
tail
Strategy
Identify one atomic step where method happens
Critical section Machine instruction
147
Linearizability: Summary
Powerful specification tool for shared objects Allows us to capture the notion of objects being atomic Dont leave home without it
148
So that G is equivalent to a
Where G S
Art of Multiprocessor Programming
149
Sequential Consistency
No need to preserve real-time order
Cannot re-order operations done by the same thread Can re-order non-overlapping operations done by different threads
Example
time
(5)
151
Example
q.enq(x)
time
(5)
152
Example
q.enq(x)
q.deq(y)
time
(5)
153
Example
q.enq(x)
(5)
154
Example
q.enq(x)
q.deq(y)
q.enq(y)
time
(5)
155
Example
q.enq(x)
q.deq(y)
q.enq(y)
time
(5)
156
Example
q.enq(x)
q.deq(y)
q.enq(y)
time
(5)
157
Theorem
Sequential Consistency is not a local property
(and thus we lose composability)
158
time
Art of Multiprocessor Programming 159
q.enq(y)
p.enq(y)
q.deq(x)
time
Art of Multiprocessor Programming 160
q.enq(y)
p.enq(y)
q.deq(x)
History H
time
Art of Multiprocessor Programming 161
q.enq(y)
p.enq(y)
q.deq(x)
time
Art of Multiprocessor Programming 162
q.enq(y)
p.enq(y)
q.deq(x)
time
Art of Multiprocessor Programming 163
Ordering imposed by p
p.enq(x)
q.enq(x)
p.deq(y) q.deq(x)
q.enq(y)
p.enq(y)
time
Art of Multiprocessor Programming 164
Ordering imposed by q
p.enq(x)
q.enq(x)
p.deq(y) q.deq(x)
q.enq(y)
p.enq(y)
time
Art of Multiprocessor Programming 165
p.enq(x)
q.enq(x)
p.deq(y) q.deq(x)
q.enq(y)
p.enq(y)
time
Art of Multiprocessor Programming 166
Combining orders
p.enq(x)
q.enq(x)
p.deq(y) q.deq(x)
q.enq(y)
p.enq(y)
time
Art of Multiprocessor Programming 167
Fact
Most hardware architectures dont support sequential consistency Because they think its too strong Heres another story
168
y.write(1)
time
Art of Multiprocessor Programming 169
y.write(1)
y.write(1)
time
y.write(1)
Its non-negotiable!
Art of Multiprocessor Programming 173
174
Memory Hierarchy
On modern multiprocessors, processors do not read and write directly to memory. Memory accesses are very slow compared to processor speeds, Instead, each processor reads and writes directly to a cache
Art of Multiprocessor Programming 175
Memory Operations
To read a memory location,
load data into cache.
176
177
Revisionist History
Flag violation history is actually OK
processors delay writing to memory until after reads have been issued.
Otherwise unacceptable delay between read and write instructions. Who knew you wanted to synchronize?
178
179
Explicit Synchronization
Memory barrier instruction
Flush unwritten caches Bring caches up to date
Expensive
180
Volatile
In Java, can ask compiler to keep a variable up-to-date with volatile keyword Also inhibits reordering, removing from loops, & other optimizations
181
Linearizability
Linearizability
Operation takes effect instantaneously between invocation and response Uses sequential specification, locality implies composablity Good for high level objects
183
Correctness: Linearizability
Sequential Consistency
Not composable Harder to work with Good way to think about hardware models
We will use linearizability as in the remainder of this course unless stated otherwise
184
Progress
We saw an implementation whose methods were lock-based (deadlockfree) We saw an implementation whose methods did not use locks (lock-free) How do they relate?
185
Progress Conditions
Deadlock-free: some thread trying to acquire the lock eventually succeeds. Starvation-free: every thread trying to acquire the lock eventually succeeds. Lock-free: some thread calling a method eventually returns. Wait-free: every thread calling a method eventually returns.
186
Progress Conditions
Non-Blocking Everyone makes progress Someone makes progress Blocking
Wait-free Lock-free
Starvation-free Deadlock-free
187
Summary
We will look at linearizable blocking and non-blocking implementations of objects.
188
189