Low level concurrency Reinventing vehicle Olena Syrota
Jul 27, 2015
Motivation• Where is computer science in software
engineering?• Not technologies• Real computer science• Algorithms• Algorithm’s properties
Scene
• Alice and Bob are neighbors• They share a yard• Alice has a cat• Bob has a dog
• Shared yard – is shared resource
We have to invent protocol for mutual exclusive access to resource (mutual exclusion)
Protocol 1 - asking
• Shouting, Knocking on the door, Cell phone
• Protocol: • Ask if I can release the pet
• Good• Comparing with protocol 0 pets can
reach the yard
• Bad:• If Alice is knocking to the Bob’s door
(shouting, dialing) and• Bob is not at home or does not hear,
turned off phone etc
Two problems
• Message can be lost• if other side is
unreachable at the moment it will never know about our intention
• Protocol requires interaction with other side. • Other side can be
unreachable. • When translating to
computer “language” we will spend time of other side to solve task. This is overhead.
Properties - waiting• Waiting• Can we reduce time of waiting, and wait only when
resource is busy but not when counterpart is not available?
• We need to persist intention to lock shared resource to reduce waiting
Protocol 2 - cans
Cans (one or several) are on the Bob’s windowsill
• The protocol is:• Alice
• Alice yanks the string to knock over of the cans
• When Bob notices that can is knocked over, he resets the can
• Alice release the cat
• Bob• Bob release the dog
whenever he needs it
• Good:• Alice’s intension is “persistent”
• Bad:• Bob can be out of home• Bob can notice that can is
knocked over too late• Not-fair protocol
Problem
This protocol divert counterpart and we are still waiting
Protocol 3 – flagsBob
1. He raises his flag.2. Look at Alice’s flag. If
is lowered, he unleashes his dog.
3. When his dog comes back, he lowers his flag.
Alice
1. She raises her flag.2. Look at Bob’s flag. If is
lowered, she unleashes her cat.
3. When her cat comes back, she lowers her flag.
What is wrong? If Alice and Bob will do steps simultaneously the pets will never reach the yard. Livelock.
Propeties - livelock• Livelock. Livelocked threads are unable to make
further progress. However, the threads are not blocked — they are simply too busy responding to each other to resume work.
Protocol 3 – flags. With priority.Bob (gives way to Alice to avoid live-lock)
1. He raises his flag.2. While Alice’s flag is raised
1. Bob lowers his flag2. Bob waits until Alice’s flag is
lowered3. Bob raises his flag
3. As soon as his flag is raised and hers is down, he unleashes his dog.
4. When his dog comes back, he lowers his flag.
Alice
1. She raises her flag.2. When Bob’s flag is
lowered, she unleashes her cat.
3. When her cat comes back, she lowers her flag.
This is not fair algorithm since it give priority to Alice. Bob’s dog can
wait forever with this approach. Starvation.
What is wrong?
Code itboolean flag[];flag[0] = false; flag[1] = false;
Bob:flag[1] = true;if (flag[0] == true) { flag[1] = false; while (flag[0] == true) { } flag[1] = true;}// critical section...flag[1] = false;// remainder section
Alice:flag[0] = true;while (flag[1] == true) {}
// critical section...flag[0] = false;// remainder section
Properties - starvation• Starvation - a thread is unable to gain regular
access to shared resources and is unable to make progress. This happens when shared resources are made unavailable for long periods by "greedy" threads
Dekker’s algorithm• Last slides we presented non-fair algorithm• Fair variant is called Dekker’s algorithm• priority is turned every time when lock needed
• Authors of Dekker’s algorithm: Dekker, Dijkstra
• It allows two threads to share a single-use resource without conflict, using only shared memory for communication
Dekker’s algorithms(var “turn” introduced to change priority)boolean flag[];flag[0] = false; flag[1] = false; int turn = 0 // or 1
P0:flag[0] = true;while (flag[1] == true) { if (turn ≠ 0) { flag[0] = false; while (turn ≠ 0) { // busy wait } flag[0] = true; }}// critical section...turn = 1;flag[0] = false;// remainder section
P1:flag[1] = true;while (flag[0] == true) { if (turn ≠ 1) { flag[1] = false; while (turn ≠ 1) { // busy wait } flag[1] = true; }}// critical section...turn = 0;flag[1] = false;// remainder section
Properties of good lock-protocol• Mutual Exclusion
• Resource is not used at the same time
• Deadlock freedom• if one pet wants to enter the yard, then it eventually succeeds • if both pets want to enter the yard, then eventually at least one of them
succeeds
• Starvation freedom• if a pet wants to enter the yard, will it eventually succeed?• If resource was locked, will it be locked out?
• Livelock-freedom• livelock is a special case of starvation. This property is proved via resource
starvation
• Waiting• Imagine that Alice raises her flag, and is then suddenly stricken with
appendicitis. The protocol states that Bob (and his dog) must wait for Alice to lower her flag. If Alice is delayed, then Bob is also delayed
Deadlock• For API users deadlock is:• Deadlock describes a situation where two or more threads are
blocked forever, waiting for each other.• Deadlock is easy to understand with 2 threads and 2+ shared
resources
• But we are protocol developers (not API users). We need deadlock-freedom property for lock
• The API developers meaning of deadlock is: If some thread attempts to acquire the lock, then some thread will succeed in acquiring the lock. If thread A calls lock() but never acquires the lock, then other threads must be completing an infinite number of critical sections
Summary
Property/Protocol “Flags” With priority
Dekker’s
Mutual Exclusion + +
Deadlock freedom + +
Starvation freedom - +
Starvation free -> deadlock free
• Mutual exclusion between two threads can be solved (however imperfectly) using only two one-bit variables, each of which can be written by one thread and read by the other.
Concurrent prime-numbers
// shared by all threads1 Counter counter = new Counter(1);
// next function is executed in parallel2 void primePrint() { 3 long i = 0;4 long limit = power(10, 10);5 while (i < limit) {6 i = counter.getAndIncrement(); 7 if (isPrime(i))8 print(i);9 }10 }
class Counter {
private int cntr;
private Lock mutex;
…
public int getAndIncrement() {
mutex.lock();
try {
cntr++; // body
} finally {
mutex.unlock();
}
}
}
LockOne algorithm• Only for 2 threads• Thread Ids: 0, 1• i = ThreadID.get()• Other thread’s id j=1-i
LockOne algorithm (2-Thread Solution)
1 class LockOne implements Lock { // thread-local index, 0 or 12 private boolean[] flag = new boolean[2];
4 public void lock() {5 int i = ThreadID.get();6 int j = 1 - i;7 flag[i] = true;8 while (flag[j]) {} // wait9 }
10 public void unlock() {11 int i = ThreadID.get();12 flag[i] = false;13 }14 }
Lock and unlock functions can be
called concurrently and race condition
can occur
LockOne algorithm (2-Thread Solution)
1 class LockOne implements Lock { // thread-local index, 0 or 12 private boolean[] flag = new boolean[2];
4 public void lock() {5 int i = ThreadID.get();6 int j = 1 - i;7 flag[i] = true;8 while (flag[j]) {} // wait9 }
10 public void unlock() {11 int i = ThreadID.get();12 flag[i] = false;13 }14 }
Thread 0
Two threads call lock/unlocksimultaneously
Thread 1
Properties of LockOne
• Mutual exclusion• Deadlock free
• If writeA(flag[A] = true) and writeB(flag[B] = true) events occur before readA(flag[B]) and readB(flag[A]) events, then both threads wait forever.
• LockOne has an interesting property: if one thread runs before the other, no deadlock occurs, and all is well.
1 class LockOne implements Lock {
// thread-local index, 0 or 1
2 private boolean[] flag = new boolean[2];
4 public void lock() {
5 int i = ThreadID.get();
6 int j = 1 - i;
7 flag[i] = true;
8 while (flag[j]) {} // wait
9 }
10 public void unlock() {
11 int i = ThreadID.get();
12 flag[i] = false;
13 }
14 }
LockTwo1 class LockTwo implements Lock {
2 private volatile int victim;
3 public void lock() {
4 int i = ThreadID.get();
5 victim = i; // let the other go first
6 while (victim == i) {} // wait
7 }
8 public void unlock() {}
9 }
LockTwo
1 class LockTwo implements Lock {
2 private volatile int victim;
3 public void lock() {
4 int i = ThreadID.get();
5 victim = i; // let the other go first
6 while (victim == i) {} // wait
7 }
8 public void unlock() {}
9 }
Thread 0
One thread runs completely before other
Thread 1
LockTwo
1 class LockTwo implements Lock {
2 private volatile int victim;
3 public void lock() {
4 int i = ThreadID.get();
5 victim = i; // let the other go first
6 while (victim == i) {} // wait
7 }
8 public void unlock() {}
9 }
• Mutual exclusion• Deadlock free
• it deadlocks if one thread runs completely before the other
• LockTwo has an interesting property: if the threads run concurrently, the lock() method succeeds
Peterson Lock1 class Peterson implements Lock {
2 // thread-local index, 0 or 1
3 private volatile boolean[] flag = new boolean[2];
4 private volatile int victim;
5 public void lock() {
6 int i = ThreadID.get();
7 int j = 1 - i;
8 flag[i] = true; // I’m interested
9 victim = i; // you go first
10 while (flag[j] && victim == i) {}; // wait
11 }
12 public void unlock() {
13 int i = ThreadID.get();
14 flag[i] = false; // I’m not interested
15 }
16 }
Lamport’s Bakery Algorithm• first-come-first-served
• When a thread wants to enter its critical section, it sets a flag, and takes a number greater than the numbers of all other threads.
• When all lower numbers have been served, the thread can enter.
• At leaving its critical section, the thread resets its flag.
Lamport’s Bakery Algorithm1 class Bakery implements Lock {2 boolean[] flag;3 Label[] label;4 public Bakery (int n) {5 flag = new boolean[n];6 label = new Label[n];7 for (int i = 0; i < n; i++) {8 flag[i] = false; label[i] = 0;9 }10 }11 public void lock() {12 int i = ThreadID.get();13 flag[i] = true;14 label[i] = max(label[0], ...,label[n-1]) + 1;15 while (( k != i)(flag[k] && (label[k],k) << (label[i],i))) {};16 }17 public void unlock() {18 flag[ThreadID.get()] = false;19 }20 }
flag[A] - whether A wants toenter the critical sectionlabel[A] - indicates the thread’srelative order when entering the bakery<< lexicographical order
(label[i], i) << (label[j], j)if and only if
label[i] < label[j] or label[i] = label[j] and i < j
Properties of Bakery Algorithm• Mutual exclusion• Starvation free• Deadlock free• First-come-first-served
• Note that any algorithm that is both deadlock-free and first-come-first-served is also starvation-free.
We considered• We considered• Dekker’s• Peterson• Lamport’s Bakery
• We did not considered compareAndSet approach, atomics, STM, actors