Professional Documents
Culture Documents
Synchronization
Lecture 5
Michael O’Boyle
1
Temporal relations
• In absence of synchronization,
– instructions executed by distinct threads must be considered
unordered / simultaneous
– Not X < X’, and not X’ < X
2
Example
Example: In the beginning...
main()
Y-axis is “time.”
A
Could be one CPU, could
pthread_create()
be multiple CPUs (cores).
foo()
B
A'
•A<B<C
B' • A' < B'
C • A < A'
• C == A'
• C == B'
3
Critical Sections / Mutual Exclusion
4
Critical sections
Critical sections
is the “happens-before” relation
T1 T2 T1 T2 T1 T2
5
When do critical sections arise?
• Shared variable:
– Globals and heap-allocated variables
– NOT local variables (which are on the stack)
6
Race conditions
• Typical symptoms
– I run it on the same data, and sometimes it prints 0 and sometimes it
prints 4
– I run it on the same data, and sometimes it prints 0 and sometimes it
crashes
7
Example: shared bank account
8
• Assume the bank’s application is multi-threaded
• A random thread is assigned a transaction when that
transaction is submitted
int withdraw(account, amount) { int withdraw(account, amount) {
int balance = get_balance(account); int balance = get_balance(account);
balance -= amount; balance -= amount;
put_balance(account, balance); put_balance(account, balance);
spit out cash; spit out cash;
} }
9
Interleaved schedules
11
How About Now?
• Morals:
– Interleavings are hard to reason about
• We make lots of mistakes
• Control-flow analysis is hard for tools to get right
– Identifying critical sections and ensuring mutually exclusive access
can make things easier
12
Another example
i++; i++;
13
Correct critical section requirements
14
Implementing Mutual Exclusion
Implementing Mutual
Implementing MutualExclusion
Exclusion
Implementing
How do we do it? Mutual Exclusion
How do we do it? I via hardware: special machine instructions
How do we do it? I via OS support: OS provides primitives via system call
I via hardware: special machine
I via software: instructions
entirely by user code
I via hardware: special machine instructions
I via OS support:HowOS provides primitives via system call
Of course, OS support needs internal hardware or software implementation.
I do we do it in software?
via OS support: OS provides primitives via system call
I via software: entirely
We assumeby
that user
mutual code
exclusion exists in hardware, so that memory
I via software: entirely by user
access is atomic: code
only one read or write to a given memory location at a
time. (True in almost all architectures.) (Exercise: is such an assumption
Of course, OS support needs internal hardware or software implementation.
necessary?)
Of course, OS support needs internal hardware or software implementation.
How do
How do we
we do
do it
it inin software?
software?
We will now try to develop a solution for mutual exclusion of two processes,
P0 and P1 . (Let ı̂ mean 1 i.)
We assume
We assume that
that mutual
mutual
Exercise: exclusion
is it (a) true, (b)exists
exclusion in hardware,
obvious, that
exists in hardware, so that
so that
doing it for two processes is memory
memory
enough?
access is
access is atomic:
atomic: only only one
one readread or or write
write to to aa given
given memory
memory locationlocation at
at aa
time. (True
time. (True inin almost
almost all all architectures.)
architectures.) (Exercise: (Exercise: isis such such an an assumption
78 / 184
assumption
necessary?)
We will now try to to develop
develop aa solution
solution for for mutual
mutual exclusion
exclusionof oftwo
twoprocesses,
processes,
P00 and P11. (Let
(Let ı̂ı̂ mean
mean 11 ii.) .)
Exercise:
Exercise: isis it
it (a)
(a) true,
true, (b)(b) obvious,
obvious, that that doingdoing itit for for twotwo processes
processes isis
enough?
enough?
15
78 / 184
Mutex – first attempt
Mutex – first attempt
Suppose we have a global variable turn. We could say that when
Pi wishes to enter critical section, it loops checking turn, and can
proceed i↵ turn = i. When done, flips turn. In pseudocode:
while ( turn != i ) { }
/* critical section */
turn = ı̂;
This has obvious problems:
I processes busy-wait
I the processes must take strict turns
although it does enforce mutex.
16
Mutex - Second attempt
Mutex – second attempt
Need to keep state of each process, not just id of next process.
So have an array of two boolean flags, flag[i], indicating whether Pi is
in critical. Then Pi does:
while ( flag[ı̂] ) { }
flag[i] = true;
/* critical section */
flag[i] = false;
This doesn’t even enforce mutex: P0 and P1 might check each other’s
flag, then both set own flags to true and enter critical section.
17
Mutex – Third attempt
Mutex – third attempt
Maybe set one’s own flag before checking the other’s?
flag[i] = true;
while ( flag[ı̂] ) { }
/* critical section */
flag[i] = false;
18
Mutex – Fourth attempt
Mutex – fourth attempt
Deadlock arose because processes insisted on entering critical section and
busy-waited. So if other process’s flag is set, let’s clear our flag for a bit
to allow it to proceed:
flag[i] = true;
while ( flag[ı̂] ) {
flag[i] = false;
/* sleep for a bit */
flag[i] = true;
}
/* critical section */
flag[i] = false;
OK, but now it is possible for the processes to run in exact synchrony and
keep deferring to each other – livelock.
19
Mutex – Peterson’s
Peterson’s Algorithmalgorithm
Peterson came up with a much simpler and more elegant (and
able) algorithm.
flag[i] = true;
turn = ı̂;
while ( flag[ı̂] && turn == ı̂ ) { }
/* critical section */
flag[i] = false;
20
Mechanisms for building critical sections
• Spinlocks
– primitive, minimal semantics; used to build others
• Semaphores (and non-spinning locks)
– basic, easy to get the hang of, somewhat hard to program with
• Monitors
– higher level, requires language support, implicit operations
– easier to program with; Java “synchronized()” as an example
• Messages
– simple model of communication and synchronization based on
(atomic) transfer of data across a channel
– direct application to distributed systems
21
Locks
22
Locks: Example
Locks: Example execution
lock()
lock() Two choices:
• Spin
• Block
unlock() • (Spin-then-block)
unlock()
23
Acquire/Release
24
Using locks
acquire(lock)
balance = get_balance(account);
int withdraw(account, amount) {
balance -= amount;
acquire(lock);
balance = get_balance(account); acquire(lock)
section
critical
balance -= amount; put_balance(account, balance);
put_balance(account, balance); release(lock);
release(lock);
balance = get_balance(account);
spit out cash;
balance -= amount;
}
put_balance(account, balance);
release(lock);
spit out cash;
25
Spinlocks
27
Implementing spinlocks
28
Spinlocks: Hardware Test-and-Set
29
Implementing spinlocks using Test-and-Set
30
Reminder of use …
acquire(lock)
balance = get_balance(account);
int withdraw(account, amount) {
balance -= amount;
acquire(lock);
balance = get_balance(account); acquire(lock)
section
critical
balance -= amount; put_balance(account, balance);
put_balance(account, balance); release(lock);
release(lock);
balance = get_balance(account);
spit out cash;
balance -= amount;
}
put_balance(account, balance);
release(lock);
spit out cash;
32
Summary
33