Next: mutual-exclusion
Alice and Bob share a yard. Alice has a cat and Bob has a dog, who don’t get along.
They could use cans tied by string to signal to each other (i.e. when their animal is in the yard).
Unfortunately, one of them could be on vacation, so this solution can deadlock.
Another solution is to set up a flagpole, where both of them can raise or lower a flag.
If Alice raises a flag:
If Bob raises his flag:
To prove this, a proof by contradiction is done:
Assume the pets could be in the yard together. Since our protocol requires that Bob waits on Alice, if Alice raises her flag, and releases her cat, she would’ve seen that Bob’s flag was not raised after raising her flag. As well, Bob waits until he raises his flag before checking Alice’s flag. If this is the case, he lowers his flag, so he would not have released his dog, so both pets cannot be in the yard at the same time.
This protocol is enough for mutual exclusion, which excludes both pets from being in the yard at the same time. This protocol also has some other properties: deadlock-freedom, where if a pet wants to enter the yard, it eventually succeeds. As well, if both pets want to enter the yard, then at least one of them eventually succeeds.
However, this protocol is not starvation free. Bob defers to Alice, so Bob’s dog could wait infinitely long before getting a turn.
As well, this protocol is bad for waiting. Imagine that Alice raises her flag, then goes to the hospital. She returns a week later, and then lowers her flag. During that time, Bob’s dog cannot use the yard, making this protocol bad for Bob.
There are two ways of communicating in concurrent systems:
Mutual exclusion requires persistent communication.
Transient communication is used in Operating Systems as interrupts – a Thread A can set a bit that Thread B reads often, which Thread B would react to. Afterwards, Thread B would reset the bit.
Interrupts cannot solve mutual exclusion, but are useful.
Assume Bob and Alice marry and divorce. The judge gives custody to Alice, and orders Bob to feed them. The pets side with Alice, and attack Bob whenever they see him. Thus, Bob must leave food for the pets in the yard while they are not there, signal to Alice that the food is there, allow Bob to leave, before Alice can release the pets.
This is the producer-consumer problem, and the cans on strings solve this problem.
Alice does:
Bob does:
This solves for the following properties:
Unfortunately, the protocol does not solve the waiting problem – If Bob deposits food in the yard and then goes on vacation without resetting the can, the pets may starve despite food being in the yard.
Imagine Bob wants to send messages to Alice about the pets. Bob might write:
sell the cat
and Alice might transcribe:
sell the
and Bob might write out a new message:
wash the dog
which Alice would finish transcribing:
sell the dog
They could use mutual exclusion to make sure that Alice only reads complete sentences, at the cost of missing some sentences.
Or they could use the can-and-string protocol, with Bob producing sentences and Alice consuming them.
This can be solved without waiting, using other protocols.
You might expect an upgrade from a single processor to multiprocessor to result in an n-way speedup. Unfortunately, it doesn’t, due to Amdahl’s law. Basically, the less distributed the work is, and how much communication overhead there is. For example, 10 painters painting rooms, with one room being twice the size only has a 5.5-fold speedup.
Exercise 1.1. The dining philosophers problem was invented by E.W. Dijkstra, a con- currency pioneer, to clarify the notions of deadlock- and starvation-freedom. Imagine five philosophers who spend their lives just thinking and feasting on rice. They sit around a circular table, illustrated in Fig. 1.5. However, there are only five chopsticks (forks, in the original formulation). Each philosopher thinks. When he gets hungry, he picks up the two chopsticks closest to him. If he can pick up both chopsticks, he can eat for a while. After a philosopher finishes eating, he puts down the chopsticks and again starts to think.
use arr_macro::arr;
use lazy_static::lazy_static;
use std::sync::{Arc, Mutex};
use std::thread;
use std::time::Duration;
lazy_static! {
// the amount of philosophers you want
static ref FORKS: Arc<[Mutex<()>]> = Arc::new(arr![Mutex::new(()); 50]);
}
#[derive(Clone)]
struct Table {
: Vec<(usize, usize, usize)>,
philosophers}
impl Table {
fn new() -> Self {
let mut philosophers = vec![];
let count = FORKS.len();
// make the last philosopher left handed
for i in 1..=count {
.push((
philosophers.saturating_sub(1),
i.saturating_sub(1) % count.saturating_sub(1),
iif i < count {
% count.saturating_add(1)
i } else {
- 1
i },
;
))}
{ philosophers }
Table }
fn eat(&self, index: usize) {
let duration = Duration::from_millis(100);
let _left = FORKS[self.philosophers[index].1].lock().unwrap();
thread::sleep(duration);
let _right = FORKS[self.philosophers[index].2].lock().unwrap();
println!("{} is eating.", self.philosophers[index].0);
thread::sleep(duration);
println!("{} is done eating.", self.philosophers[index].0);
}
}
fn main() {
let table = Table::new();
let round_duration = Duration::from_millis(1000);
let mut round = 1;
loop {
println!("Round {}", round);
thread::sleep(round_duration);
let handles: Vec<_> = table
.philosophers
.clone()
.into_iter()
.enumerate()
.map(|(index, _)| {
let table = table.clone();
thread::spawn(move || {
.eat(index);
table})
})
.collect();
for handle in handles {
.join().unwrap();
handle}
+= 1;
round }
}
Exercise 1.2. For each of the following, state whether it is a safety or liveness property. Identify the bad or good thing of interest.
Safety: a bad thing that will never happen is that patrons are served out of order.
Liveness: something will always go wrong.
Safety: a correctness property
Liveness: two things must occur
Liveness: progress is always made toward death
Safety:
Safety
Liveness: progress is made toward what darth vader started
Safety: an invariant
Safety
Exercise 1.3. In the producer-consumer fable, we assumed that Bob can see whether the can on Alice’s windowsill is up or down. Design a producer-consumer protocol using cans and strings that works even if Bob cannot see the state of Alice’s can (this is how real-world interrupt bits work).
Have a two cans connected by a string on both Bob and Alice’s windowsill.
Initially, both cans are standing up.
When Alice wants to release the pets:
For Bob:
Exercise 1.4:
You are one of P recently arrested prisoners. The warden, a deranged computer scientist, makes the following announcement: you may meet together today and plan a strategy, but after today you will be in isolated cells and have no communication with one another. I have set up a “switch room” which contains a light switch, which is either on or off. The switch is not connected to anything. Every now and then, I will select one prisoner at random to enter the “switch room.” This prisoner may throw the switch (from on to off, or vice-versa), or may leave the switch unchanged. Nobody else will ever enter this room. Each prisoner will visit the switch room arbitrarily often. More precisely, for any N, eventually each of you will visit the switch room at least N times. At any time, any of you may declare: “we have all visited the switch room at least once.” If the claim is correct, I will set you free. If the claim is incorrect, I will feed all of you to the crocodiles. Choose wisely.
If the state is off:
Select one person to be the consumer and the others as producers.
The producers follow this protocol:
The consumer then:
If the initial state is either on or off, then you should do the same, except producers turn on the light twice, and the consumer counts to (2 * N - 1)
Exercise 1.5. The same warden has a different idea. He orders the prisoners to stand in line, and places red and blue hats on each of their heads. No prisoner knows the color of his own hat, or the color of any hat behind him, but he can see the hats of the prisoners in front. The warden starts at the back of the line and asks each prisoner to guess the color of his own hat. The prisoner can answer only “red” or “blue.” If he gives the wrong answer, he is fed to the crocodiles. If he answers correctly, he is freed. Each prisoner can hear the answer of the prisoners behind him, but cannot tell whether that prisoner was correct. The prisoners are allowed to consult and agree on a strategy beforehand (while the warden listens in) but after being lined up, they cannot communicate any other way besides their answer of “red” or “blue.” Devise a strategy that allows at least P − 1 of P prisoners to be freed.
The prisoners need to do the following:
The first prisoner counts the number of red and blue hats. If red is odd, he says red, otherwise blue. The next prisoner then counts the number of red hats. If the number is odd, but the previous prisoner said red, he knows he has a blue hat. He says blue. Otherwise, he has a red hat, so he says red. Every other prisoner follows the same protocol.
Exercise 1.6. A financial risk management program is sped up by making 85% of the application concurrent, while 15% remains sequential. However, it turns out that during a concurrent execution the number of cache misses grows in a way dependent on N , the number of cores used. The dependency is CacheMiss = N N +10 . Profiling the program reveals that 20% of the operations performed are memory accesses for both the sequential and parallel parts. The cost of other operations, including cache accesses, is 1 unit, and accessing memory has a cost of 3N + 11 units for the parallel part and a cost of 14 for the sequential part. Compute the optimal number of processors on which the program should run.
Exercise 1.9 You have a choice between buying one uniprocessor that executes five zillion instructions per second or a 10-processor multiprocessor where each processor executes one zillion instructions per second. Using Amdahl’s law, explain how you would decide which to buy for a particular application.
Next: mutual-exclusion