










Study with the several resources on Docsity
Earn points by helping other students or get them with a premium plan
Prepare for your exams
Study with the several resources on Docsity
Earn points to download
Earn points by helping other students or get them with a premium plan
The solution to the too much milk problem using locks and discusses different ways of implementing locks, including with test and set instructions. The document also introduces semaphores as a higher-level synchronization primitive.
Typology: Slides
1 / 18
This page cannot be seen from the preview
Don't miss anything!











Too Much Milk Summary
Solution #3 works, but it's really unsatisfactory: 1. really complicated -- even for this simple an example, hard to convince yourself it really works
2. A's code different than B's -- what if lots of threads? Code would have to be slightly different for each thread.
3. While A is waiting, it is consuming CPU time ( busywaiting ) There's a better way: use higher-level atomic operations; load and store are too primitive.
Lock: prevents someone from doing something.
Lock::Acquire -- wait until lock is free, then grab it
Lock::Release -- unlock, waking up a waiter if any
These must be atomic operations -- if two threads are waiting for the lock, and both see it's free, only one grabs it! With locks, the too much milk problem becomes really easy!
lock->Acquire();
if (nomilk)
buy milk;
lock->Release();
A flawed, but very simple implementation
Lock::Acquire() { disable interrupts;} Lock::Release() { enable interrupts; }
Busy-waiting implementation
class Lock { int value = FREE;
Lock::Acquire() { Disable interrupts; while (value != FREE) { Enable interrupts; // allow interrupts Disable interrupts; }
value = BUSY; Enable interrupts; }
Lock::Release() { Disable interrupts; value = FREE; Enable interrupts; } }
Implementing without busy-waiting (1)
Lock::Acquire() { Disable interrupts; while (value != FREE) { put on queue of threads waiting for lock change state to sleeping or blocked } value = BUSY; Enable interrupts; } Lock::Release() { Disable interrupts; if anyone on wait queue { take a waiting thread off put it on ready queue change its state to ready } value = FREE; Enable interrupts; }
Implementing without busy-waiting (2)
When does Acquire re-enable interrupts :
In going to sleep?
Before putting the thread on the wait queue?
Then Release can check queue, and not wake the thread up.
After putting the thread on the wait queue, but before going to
sleep?
Then Release puts thread on the ready queue, When thread
wakes up, it will go to sleep, missing the wakeup from Release.
In other words, putting the thread on wait queue and going to
sleep must be done atomically before re-enabling interrupts
Implementing locks with test&set
Test&set reads location, sets it to 1, and returns old value. Initially, lock value = 0; Lock::Acquire { while (test&set(value) == 1) ; // Do nothing } Lock::Release { value = 0; }
If lock is free, test&set reads 0 and sets value to 1, so lock is now busy. It returns 0, so Acquire completes. If lock is busy, test&set reads 1 and sets value to 1 (no change), so lock stays busy, and Acquire will loop. This is a busy-wait loop, but as with the discussion above about disable interrupts, you can modify it to sleep if lock is BUSY.
Semaphores
semaphore = a synchronization primitive
A semaphore is:
A pseudocode implementation
Binary semaphore (aka mutex semaphore)
Counting semaphore (aka counted semaphore)
N = number of units available
Only operations are P and V -- can't read or write value, except to set it initially
Operations must be atomic -- two P's that occur together
can't decrement the value below zero.