implementing mutual exclusion
DESCRIPTION
Implementing Mutual Exclusion. Sarah Diesburg Operating Systems COP 4610. From the Previous Lecture. The “too much milk” example shows that writing concurrent programs directly with load and store instructions (i.e., C assignment statements) is tricky - PowerPoint PPT PresentationTRANSCRIPT
Implementing Mutual Implementing Mutual ExclusionExclusionSarah DiesburgSarah Diesburg
Operating SystemsOperating Systems
COP 4610COP 4610
From the Previous LectureFrom the Previous Lecture
The “too much milk” example shows The “too much milk” example shows that writing concurrent programs that writing concurrent programs directly with load and store directly with load and store instructions (i.e., C assignment instructions (i.e., C assignment statements) is trickystatements) is tricky
Programmers want to use higher-Programmers want to use higher-level operations, such as lockslevel operations, such as locks
Ways of Implementing LocksWays of Implementing Locks
Locking primitives
High-level atomic operations
Locks, semaphores, Locks, semaphores, monitors, send and monitors, send and receivereceive
Low-level atomic operations
Load/store, interrupt Load/store, interrupt disables, disables, test_and_settest_and_set
All implementations require some All implementations require some level of hardware supportlevel of hardware support
Atomic Memory Load and StoreAtomic Memory Load and Store
C assignment statementsC assignment statements Examples: “too much milk” solutionsExamples: “too much milk” solutions
Disable Interrupts (for Disable Interrupts (for Uniprocessors)Uniprocessors)
On a uniprocessor,On a uniprocessor,– An operation is atomic as long as a An operation is atomic as long as a
context switch does not occur in the context switch does not occur in the middle of an operationmiddle of an operation
Solution 1Solution 1Lock::Acquire() {Lock::Acquire() {
// disable interrupts;// disable interrupts;}}Lock::Release() {Lock::Release() {
// enable interrupts;// enable interrupts; }}
Problems with Solution 1Problems with Solution 1
A user-level program may not re-A user-level program may not re-enable interruptsenable interrupts– The kernel can no longer regain the The kernel can no longer regain the
controlcontrol No guarantees on the duration of No guarantees on the duration of
interrupts; bad for real-time systemsinterrupts; bad for real-time systems Solution 1 will not work for more Solution 1 will not work for more
complex scenarios (nested locks)complex scenarios (nested locks)
Solution 2Solution 2class Lock {class Lock {
int value = FREE;int value = FREE;}}
Lock::Acquire() {Lock::Acquire() {// disable interrupts// disable interruptswhile (value != FREE) {while (value != FREE) {
// enable interrupts// enable interrupts// disable interrupts// disable interrupts
}}value = BUSY;value = BUSY;// enable interrupts// enable interrupts
}}
Lock::Release() {Lock::Release() {
// disable interrupts// disable interrupts
value = FREE;value = FREE;
// enable interrupts// enable interrupts
}}
Solution 2Solution 2class Lock {class Lock {
int value = FREE;int value = FREE;}}
Lock::Acquire() {Lock::Acquire() {// disable interrupts// disable interruptswhile (value != FREE) {while (value != FREE) {
// enable interrupts// enable interrupts// disable interrupts// disable interrupts
}}value = BUSY;value = BUSY;// enable interrupts// enable interrupts
}}
Lock::Release() {Lock::Release() {
// disable interrupts// disable interrupts
value = FREE;value = FREE;
// enable interrupts// enable interrupts
}}
The lock is initially FREE.
Solution 2Solution 2class Lock {class Lock {
int value = FREE;int value = FREE;}}
Lock::Acquire() {Lock::Acquire() {// disable interrupts// disable interruptswhile (while (value != FREEvalue != FREE) {) {
// enable interrupts// enable interrupts// disable interrupts// disable interrupts
}}value = BUSY;value = BUSY;// enable interrupts// enable interrupts
}}
Lock::Release() {Lock::Release() {
// disable interrupts// disable interrupts
value = FREE;value = FREE;
// enable interrupts// enable interrupts
}}
Check the lock value whileinterrupts are disabled.
Solution 2Solution 2class Lock {class Lock {
int value = FREE;int value = FREE;}}
Lock::Acquire() {Lock::Acquire() {// disable interrupts// disable interruptswhile (value != FREE) {while (value != FREE) {
// enable interrupts// enable interrupts// disable interrupts// disable interrupts
}}value = BUSY;value = BUSY;// enable interrupts// enable interrupts
}}
Lock::Release() {Lock::Release() {
// disable interrupts// disable interrupts
value = FREE;value = FREE;
// enable interrupts// enable interrupts
}}
Re-enable interrupts inside theloop, so someone may have a chance to unlock.
Solution 2Solution 2class Lock {class Lock {
int value = FREE;int value = FREE;}}
Lock::Acquire() {Lock::Acquire() {// disable interrupts// disable interruptswhile (value != FREE) {while (value != FREE) {
// enable interrupts// enable interrupts// disable interrupts// disable interrupts
}}value = BUSY;value = BUSY;// enable interrupts// enable interrupts
}}
Lock::Release() {Lock::Release() {
// disable interrupts// disable interrupts
value = FREE;value = FREE;
// enable interrupts// enable interrupts
}}
Disable the interrupts againbefore checking the lock.
Solution 2Solution 2class Lock {class Lock {
int value = FREE;int value = FREE;}}
Lock::Acquire() {Lock::Acquire() {// disable interrupts// disable interruptswhile (value != FREE) {while (value != FREE) {
// enable interrupts// enable interrupts// disable interrupts// disable interrupts
}}value = BUSY;value = BUSY;// enable interrupts// enable interrupts
}}
Lock::Release() {Lock::Release() {
// disable interrupts// disable interrupts
value = FREE;value = FREE;
// enable interrupts// enable interrupts
}}
If no one is holding the lock, grab the lock.
Problems with Solution 2Problems with Solution 2
It works for a single processorIt works for a single processor It does not work on a multi-processor It does not work on a multi-processor
machinemachine– Other CPUs can still enter the critical Other CPUs can still enter the critical
sectionsection
The The test_and_settest_and_set Operation Operation
test_and_settest_and_set works on works on multiprocessorsmultiprocessors– Atomically reads a memory locationAtomically reads a memory location– Sets it to 1Sets it to 1– Returns the old value of memory Returns the old value of memory
locationlocation
The The test_and_settest_and_set Operation Operation
value = 0;value = 0;
Lock::Acquire() {Lock::Acquire() {// while the previous value is BUSY, loop// while the previous value is BUSY, loopwhile (test_and_set(value) == 1);while (test_and_set(value) == 1);
}}
Lock::Release() {Lock::Release() {value = 0;value = 0;
}}
Common Problems with Mentioned Common Problems with Mentioned ApproachesApproaches
Busy-waitingBusy-waiting: consumption of CPU : consumption of CPU cycles while a thread is waiting for a cycles while a thread is waiting for a locklock– Very inefficientVery inefficient– Can be avoided with a waiting queueCan be avoided with a waiting queue
May as well sleep instead of busy-wait…
A tail of two threads…A tail of two threads…
Suppose both threads want the lock, Suppose both threads want the lock, but like to be lazy…but like to be lazy…
Thread 1: Lazy Thread 2: Lazier
Locks Using Interrupt Disables, Locks Using Interrupt Disables, Without Busy-WaitingWithout Busy-Waiting
class Lock {class Lock {int value = FREE;int value = FREE;
}}
Lock::Acquire() {Lock::Acquire() {// disable interrupts// disable interruptsif (value != FREE) {if (value != FREE) {
// Queue the thread// Queue the thread// Go to sleep// Go to sleep
} else {} else {value = BUSY;value = BUSY;
}}// enable interrupts// enable interrupts
}}
Lock::Release() {Lock::Release() {
// disable interrupts// disable interrupts
if (someone is waiting) {if (someone is waiting) {
// wake a thread// wake a thread
// Put it on ready // Put it on ready queuequeue
} else {} else {
value = FREE;value = FREE;
}}
// enable interrupts// enable interrupts
}}
Locks Using Interrupt Disables, Locks Using Interrupt Disables, Without Busy-WaitingWithout Busy-Waiting
class Lock {class Lock {int value = FREE;int value = FREE;
}}
Lock::Acquire() {Lock::Acquire() {// disable interrupts// disable interruptsifif (value != FREE) { (value != FREE) {
// Queue the thread// Queue the thread// Go to sleep// Go to sleep
} else {} else {value = BUSY;value = BUSY;
}}// enable interrupts// enable interrupts
}}
Lock::Release() {Lock::Release() {
// disable interrupts// disable interrupts
if (someone is waiting) {if (someone is waiting) {
// wake a thread// wake a thread
// Put it on ready // Put it on ready queuequeue
} else {} else {
value = FREE;value = FREE;
}}
// enable interrupts// enable interrupts
}}
Thread 1 tries to grab the lock.
Locks Using Interrupt Disables, Locks Using Interrupt Disables, Without Busy-WaitingWithout Busy-Waiting
class Lock {class Lock {int value = FREE;int value = FREE;
}}
Lock::Acquire() {Lock::Acquire() {// disable interrupts// disable interruptsifif (value != FREE) { (value != FREE) {
// Queue the thread// Queue the thread// Go to sleep// Go to sleep
} else {} else {value = BUSY;value = BUSY;
}}// enable interrupts// enable interrupts
}}
Lock::Release() {Lock::Release() {
// disable interrupts// disable interrupts
if (someone is waiting) {if (someone is waiting) {
// wake a thread// wake a thread
// Put it on ready // Put it on ready queuequeue
} else {} else {
value = FREE;value = FREE;
}}
// enable interrupts// enable interrupts
}}
No more busy waiting…
Locks Using Interrupt Disables, Locks Using Interrupt Disables, Without Busy-WaitingWithout Busy-Waiting
class Lock {class Lock {int value = FREE;int value = FREE;
}}
Lock::Acquire() {Lock::Acquire() {// disable interrupts// disable interruptsifif (value != FREE) { (value != FREE) {
// Queue the thread// Queue the thread// Go to sleep// Go to sleep
} else {} else {value = BUSY;value = BUSY;
}}// enable interrupts// enable interrupts
}}
Lock::Release() {Lock::Release() {
// disable interrupts// disable interrupts
if (someone is waiting) {if (someone is waiting) {
// wake a thread// wake a thread
// Put it on ready // Put it on ready queuequeue
} else {} else {
value = FREE;value = FREE;
}}
// enable interrupts// enable interrupts
}}
Grab the lock.
Locks Using Interrupt Disables, Locks Using Interrupt Disables, Without Busy-WaitingWithout Busy-Waiting
class Lock {class Lock {int value = FREE;int value = FREE;
}}
Lock::Acquire() {Lock::Acquire() {// disable interrupts// disable interruptsifif (value != FREE) { (value != FREE) {
// Queue the thread// Queue the thread// Go to sleep// Go to sleep
} else {} else {value = BUSY;value = BUSY;
}}// enable interrupts// enable interrupts
}}
Lock::Release() {Lock::Release() {
// disable interrupts// disable interrupts
if (someone is waiting) {if (someone is waiting) {
// wake a thread// wake a thread
// Put it on ready // Put it on ready queuequeue
} else {} else {
value = FREE;value = FREE;
}}
// enable interrupts// enable interrupts
}}
Thread 1 goes on computing.
Locks Using Interrupt Disables, Locks Using Interrupt Disables, Without Busy-WaitingWithout Busy-Waiting
class Lock {class Lock {int value = FREE;int value = FREE;
}}
Lock::Acquire() {Lock::Acquire() {// disable interrupts// disable interruptsifif (value != FREE) { (value != FREE) {
// Queue the thread// Queue the thread// Go to sleep// Go to sleep
} else {} else {value = BUSY;value = BUSY;
}}// enable interrupts// enable interrupts
}}
Lock::Release() {Lock::Release() {
// disable interrupts// disable interrupts
if (someone is waiting) {if (someone is waiting) {
// wake a thread// wake a thread
// Put it on ready // Put it on ready queuequeue
} else {} else {
value = FREE;value = FREE;
}}
// enable interrupts// enable interrupts
}}
Thread 2 tries to grab the lock.
Locks Using Interrupt Disables, Locks Using Interrupt Disables, Without Busy-WaitingWithout Busy-Waiting
class Lock {class Lock {int value = FREE;int value = FREE;
}}
Lock::Acquire() {Lock::Acquire() {// disable interrupts// disable interruptsifif (value != FREE) {(value != FREE) {
// Queue the thread// Queue the thread// Go to sleep// Go to sleep
} else {} else {value = BUSY;value = BUSY;
}}// enable interrupts// enable interrupts
}}
Lock::Release() {Lock::Release() {
// disable interrupts// disable interrupts
if (someone is waiting) {if (someone is waiting) {
// wake a thread// wake a thread
// Put it on ready // Put it on ready queuequeue
} else {} else {
value = FREE;value = FREE;
}}
// enable interrupts// enable interrupts
}}
The lock is busy…
Locks Using Interrupt Disables, Locks Using Interrupt Disables, Without Busy-WaitingWithout Busy-Waiting
class Lock {class Lock {int value = FREE;int value = FREE;
}}
Lock::Acquire() {Lock::Acquire() {// disable interrupts// disable interruptsifif (value != FREE) { (value != FREE) {
// Queue the thread// Queue the thread// Go to sleep// Go to sleep
} else {} else {value = BUSY;value = BUSY;
}}// enable interrupts// enable interrupts
}}
Lock::Release() {Lock::Release() {
// disable interrupts// disable interrupts
if (someone is waiting) {if (someone is waiting) {
// wake a thread// wake a thread
// Put it on ready // Put it on ready queuequeue
} else {} else {
value = FREE;value = FREE;
}}
// enable interrupts// enable interrupts
}}
Put the thread 2 on a waiting queue.
Locks Using Interrupt Disables, Locks Using Interrupt Disables, Without Busy-WaitingWithout Busy-Waiting
class Lock {class Lock {int value = FREE;int value = FREE;
}}
Lock::Acquire() {Lock::Acquire() {// disable interrupts// disable interruptsifif (value != FREE) { (value != FREE) {
// Queue the thread// Queue the thread// Go to sleep// Go to sleep
} else {} else {value = BUSY;value = BUSY;
}}// enable interrupts// enable interrupts
}}
Lock::Release() {Lock::Release() {
// disable interrupts// disable interrupts
if (someone is waiting) {if (someone is waiting) {
// wake a thread// wake a thread
// Put it on ready // Put it on ready queuequeue
} else {} else {
value = FREE;value = FREE;
}}
// enable interrupts// enable interrupts
}}
Sleep it off… Context switch;wait for someone to wake up the thread. zzzz
Locks Using Interrupt Disables, Locks Using Interrupt Disables, Without Busy-WaitingWithout Busy-Waiting
class Lock {class Lock {int value = FREE;int value = FREE;
}}
Lock::Acquire() {Lock::Acquire() {// disable interrupts// disable interruptsifif (value != FREE) { (value != FREE) {
// Queue the thread// Queue the thread// Go to sleep// Go to sleep
} else {} else {value = BUSY;value = BUSY;
}}// enable interrupts// enable interrupts
}}
Lock::Release() {Lock::Release() {
// disable interrupts// disable interrupts
if (someone is waiting) {if (someone is waiting) {
// wake a thread// wake a thread
// Put it on ready // Put it on ready queuequeue
} else {} else {
value = FREE;value = FREE;
}}
// enable interrupts// enable interrupts
}}
Say thread 1 wants to release the lock (interrupts are already disabled by thread 2).
Locks Using Interrupt Disables, Locks Using Interrupt Disables, Without Busy-WaitingWithout Busy-Waiting
class Lock {class Lock {int value = FREE;int value = FREE;
}}
Lock::Acquire() {Lock::Acquire() {// disable interrupts// disable interruptsifif (value != FREE) { (value != FREE) {
// Queue the thread// Queue the thread// Go to sleep// Go to sleep
} else {} else {value = BUSY;value = BUSY;
}}// enable interrupts// enable interrupts
}}
Lock::Release() {Lock::Release() {
// disable interrupts// disable interrupts
if (someone is waiting) {if (someone is waiting) {
// wake a thread// wake a thread
// Put it on ready // Put it on ready queuequeue
} else {} else {
value = FREE;value = FREE;
}}
// enable interrupts// enable interrupts
}}
Hello? Is someone waiting there? Thread 2 is waiting.
Locks Using Interrupt Disables, Locks Using Interrupt Disables, Without Busy-WaitingWithout Busy-Waiting
class Lock {class Lock {int value = FREE;int value = FREE;
}}
Lock::Acquire() {Lock::Acquire() {// disable interrupts// disable interruptsifif (value != FREE) { (value != FREE) {
// Queue the thread// Queue the thread// Go to sleep// Go to sleep
} else {} else {value = BUSY;value = BUSY;
}}// enable interrupts// enable interrupts
}}
Lock::Release() {Lock::Release() {
// disable interrupts// disable interrupts
if (someone is waiting) {if (someone is waiting) {
// wake a thread// wake a thread
// Put it on ready // Put it on ready queuequeue
} else {} else {
value = FREE;value = FREE;
}}
// enable interrupts// enable interrupts
}}
Put thread 2 on ready queue; context switch.
Locks Using Interrupt Disables, Locks Using Interrupt Disables, Without Busy-WaitingWithout Busy-Waiting
class Lock {class Lock {int value = FREE;int value = FREE;
}}
Lock::Acquire() {Lock::Acquire() {// disable interrupts// disable interruptsifif (value != FREE) { (value != FREE) {
// Queue the thread// Queue the thread// Go to sleep// Go to sleep
} else {} else {value = BUSY;value = BUSY;
}}// enable interrupts// enable interrupts
}}
Lock::Release() {Lock::Release() {
// disable interrupts// disable interrupts
if (someone is waiting) {if (someone is waiting) {
// wake a thread// wake a thread
// Put it on ready // Put it on ready queuequeue
} else {} else {
value = FREE;value = FREE;
}}
// enable interrupts// enable interrupts
}}
Thread 2: Who woke me? I don’t do mornings…
Locks Using Interrupt Disables, Locks Using Interrupt Disables, Without Busy-WaitingWithout Busy-Waiting
class Lock {class Lock {int value = FREE;int value = FREE;
}}
Lock::Acquire() {Lock::Acquire() {// disable interrupts// disable interruptsifif (value != FREE) { (value != FREE) {
// Queue the thread// Queue the thread// Go to sleep// Go to sleep
} else {} else {value = BUSY;value = BUSY;
}}// enable interrupts// enable interrupts
}}
Lock::Release() {Lock::Release() {
// disable interrupts// disable interrupts
if (someone is waiting) {if (someone is waiting) {
// wake a thread// wake a thread
// Put it on ready // Put it on ready queuequeue
} else {} else {
value = FREE;value = FREE;
}}
// enable interrupts// enable interrupts
}}
Thread 2 is done with its computation.
Locks Using Interrupt Disables, Locks Using Interrupt Disables, Without Busy-WaitingWithout Busy-Waiting
class Lock {class Lock {int value = FREE;int value = FREE;
}}
Lock::Acquire() {Lock::Acquire() {// disable interrupts// disable interruptsifif (value != FREE) { (value != FREE) {
// Queue the thread// Queue the thread// Go to sleep// Go to sleep
} else {} else {value = BUSY;value = BUSY;
}}// enable interrupts// enable interrupts
}}
Lock::Release() {Lock::Release() {
// disable interrupts// disable interrupts
if (someone is waiting) {if (someone is waiting) {
// wake a thread// wake a thread
// Put it on ready // Put it on ready queuequeue
} else {} else {
value = FREE;value = FREE;
}}
// enable interrupts// enable interrupts
}}
Suppose no one else is waiting.
Locks Using Interrupt Disables, Locks Using Interrupt Disables, Without Busy-WaitingWithout Busy-Waiting
class Lock {class Lock {int value = FREE;int value = FREE;
}}
Lock::Acquire() {Lock::Acquire() {// disable interrupts// disable interruptsifif (value != FREE) { (value != FREE) {
// Queue the thread// Queue the thread// Go to sleep// Go to sleep
} else {} else {value = BUSY;value = BUSY;
}}// enable interrupts// enable interrupts
}}
Lock::Release() {Lock::Release() {
// disable interrupts// disable interrupts
if (someone is waiting) {if (someone is waiting) {
// wake a thread// wake a thread
// Put it on ready // Put it on ready queuequeue
} else {} else {
value = FREE;value = FREE;
}}
// enable interrupts// enable interrupts
}}
Release the lock. (Thread 1 hasfinished its work, so it’s okay.)
Locks Using Interrupt Disables, Locks Using Interrupt Disables, Without Busy-WaitingWithout Busy-Waiting
class Lock {class Lock {int value = FREE;int value = FREE;
}}
Lock::Acquire() {Lock::Acquire() {// disable interrupts// disable interruptsifif (value != FREE) { (value != FREE) {
// Queue the thread// Queue the thread// Go to sleep// Go to sleep
} else {} else {value = BUSY;value = BUSY;
}}// enable interrupts// enable interrupts
}}
Lock::Release() {Lock::Release() {
// disable interrupts// disable interrupts
if (someone is waiting) {if (someone is waiting) {
// wake a thread// wake a thread
// Put it on ready // Put it on ready queuequeue
} else {} else {
value = FREE;value = FREE;
}}
// enable interrupts// enable interrupts
}}
Warp 9, engage (let’s get out of here)…
Locks Using Interrupt Disables, Locks Using Interrupt Disables, Without Busy-WaitingWithout Busy-Waiting
class Lock {class Lock {int value = FREE;int value = FREE;
}}
Lock::Acquire() {Lock::Acquire() {// disable interrupts// disable interruptsifif (value != FREE) { (value != FREE) {
// Queue the thread// Queue the thread// Go to sleep// Go to sleep
} else {} else {value = BUSY;value = BUSY;
}}// enable interrupts// enable interrupts
}}
Lock::Release() {Lock::Release() {
// disable interrupts// disable interrupts
if (someone is waiting) {if (someone is waiting) {
// wake a thread// wake a thread
// Put it on ready // Put it on ready queuequeue
} else {} else {
value = FREE;value = FREE;
}}
// enable interrupts// enable interrupts
}}
Eventually, the kernel will contextswitch back to thread 1.
What happened?
So, What’s Going On?So, What’s Going On?
Interrupt disable and enable Interrupt disable and enable operations occur across context operations occur across context switches (at the steady state)switches (at the steady state)
So, What’s Going On?So, What’s Going On?
Thread AThread A Thread BThread B
Disable interruptsSleep
Disable interruptsSleep
Return from sleepEnable interrupts
Context switch
Return from sleepEnable interrupts
Context switch
Locks Using Locks Using test_and_settest_and_set, With , With Minimal Busy-WaitingMinimal Busy-Waiting
Impossible to use Impossible to use test_and_settest_and_set to to avoid busy-waitingavoid busy-waiting
However, waiting can be minimized However, waiting can be minimized with a waiting queuewith a waiting queue
Locks Using Locks Using test_and_settest_and_set, With , With Minimal Busy-WaitingMinimal Busy-Waiting
class Lock {class Lock {int value = FREE;int value = FREE;
int guard = 0;int guard = 0;}}
Lock::Acquire() {Lock::Acquire() {while (test_and_set(guard));while (test_and_set(guard));if (value != FREE) {if (value != FREE) {
// queue the thread// queue the thread// guard = 0 and // guard = 0 and
sleepsleep} else {} else {
value = BUSY;value = BUSY;}}guard = 0;guard = 0;
}}
Lock::Release() {Lock::Release() {
while (test_and_set(guard));while (test_and_set(guard));
if (anyone waiting) {if (anyone waiting) {
// wake up one thread// wake up one thread
// put it on ready // put it on ready queuequeue
} else {} else {
value = FREE;value = FREE;
}}
guard = 0;guard = 0;
}}