Recall — Processes may be independent or cooperating; and the scheduler may interrupt mid-execution.
- Thus, maintaining data consistency amongst cooperating processes sharing data requires special mechanisms.
We introduced the producer-consumer problem back in the process management chapter, and provided a simple solution:
Producer:
item next_produced;
while(true) {
// produce an item in next_produced
while (((in + 1) % BUFFER_SIZE == out)
; // do nothing
buffer[in] = next_produced;
in = (in + 1) % BUFFER_SIZE;
}Consumer:
item next_consumed;
while (true) {
while (in == out)
; // do nothing
next_consumed = buffer[out];
out = (out + 1) % BUFFER_SIZE
// consume the item in next_consumed
}Adding a counter variable, we could track open memory spaces, allowing us to buffer more shared memory, rather than just a single variable.
Producer:
item next_produced;
while(true) {
// produce an item in next_produced
while (counter === BUFFER_SIZE == out) ;
// do nothing
buffer[in] = next_produced;
in = (in + 1) % BUFFER_SIZE;
counter++;
}Consumer:
item next_consumed;
while (true) {
while (counter == 0)
; // do nothing
next_consumed = buffer[out];
out = (out + 1) % BUFFER_SIZE
counter--;
// consume the item in next_consumed
}Without synchronization, we could get a race condition.
Suppose this producer-consumer pair interleaves execution without synchronization.
producer executes: register1 = counter
producer executes: register1 += 1
// Context Switch (producer -> consumer)
consumer executes: register2 = counter
consumer executes: register2 -= 1
// Context Switch (consumer -> producer)
producer executes: counter = register1
// Context Switch (producer -> consumer)
consumer executes: counter = register1This race condition arose because we split the critical section.
Some ways to solve this:
1. Sequential Execution
TODO
2. Interleaved Execution
TODO
Consider system of n processes.
Critical Section: Segment of code that may be changing common variables, updating tables, writing files, etc.
Critical Section Problem: Design a protocol to solve this problem.
Three Parts of a Critical Section: TODO
Two Approaches:
Example: Incorrect interleaving of code
Processes p and q execute the instructions x = x + 1 and x = x + 2, respectively. Initially, x = 0
Q: What is the value of x after execution?
p: q:load Rp, xload Rq, xadd Rp, 1add Rq, 2store Rq, xstore Rq, x
p: q:load Rq, xload Rp, xadd Rp, 1add Rq, 2store Rq, xstore Rq, x
p: q:load Rp, xadd Rp, 1load Rq, xadd Rq, 2store Rq, xstore Rq, x
A:
If you don’t synchronize, the outcome is entirely dependent on how the CPU jumps between processes.
Solutions must satisfy the following:
1. Guarantee Mutual Exclusion: Only one process can be executing its critical section.
2. Prevent Lockout: Processes not attempting to enter CS mustn’t prevent other processes from entering the CS.
3. Prevent Starvation: We can’t let the same process do its CS over-and-over and starve waiting ones.
4. Prevent Deadlock: Multiple processes trying to enter their CS at the same time mustn’t block each other indefinitely. Suppose p and q use two keys. In this state, p and q could end up waiting forever for the other to give up their key, resulting in deadlock.Example: Deadlock
p qkey1 key2key2 key1
A software solution to the critical selection problem.
do {acquire lockcritical sectionrelease lockremainder section} while (TRUE);
Protect a critical section by first acquiring a lock, and then releasing the lock.
acquire() and release() must be atomic calls (usually done via hardware atomic instructions)Atomic Call: The CPU cannot stop execution during an atomic call.
Spinlock: This lock requires busy waiting (while (!available);), so it’s also called a spinlock.Example: acquire and release
acquire() {
while (!available); // (busy wait)
available = true;
}
release() {
available = false;
}