Synchronization made easy! February 2009 Preferred Synchronization methods.
Synchronization
-
Upload
felix-wagner -
Category
Documents
-
view
14 -
download
0
description
Transcript of Synchronization
Synchronization
Where are we going with synchronization?
• We are going to implement various higher-level synchronization
primitives using atomic operations
– Everything is pretty painful if only atomic primitives are load and store
– Need to provide primitives useful at user-level
Load/Store Disable Ints Test&Set Comp&Swap
Locks Semaphores Monitors
Shared Programs
Hardware
Higher-level API
Programs
Lock
• Race conditions can be prevented by
requiring that critical sections be protected by
locks. That is, a process must acquire a lock
before entering a critical section; it releases
the lock when it exits the critical section
Synchronization
do {
[acquire lock]
critical section
[release lock]
remainder section
} while (TRUE);
“Too Much Milk” Solution #4
• Suppose we have some sort of implementation of a lock– Lock.Acquire() – wait until lock is free, then grab– Lock.Release() – Unlock, waking up anyone waiting– These must be atomic operations – if two processes are waiting for
the lock and both see it’s free, only one succeeds to grab the lock• Then, our milk problem is easy:
milklock.Acquire();if (nomilk) buy milk;milklock.Release();
How to implement Locks?
• Lock: prevents someone from doing something– Lock before entering critical section and before
accessing shared data– Unlock when leaving, after accessing shared data– Wait if locked
• Important idea: all synchronization involves waiting• Should sleep if waiting for a long time
• Hardware Lock instruction
Synchronization Hardware
• Many systems provide hardware support for critical section
code
• Uniprocessors – could disable interrupts
– Currently running code would execute without preemption
– System’s clock is kept updated by interrupts
Naïve use of Interrupt Enable/Disable• How can we build multi-instruction atomic operations?
– Recall: dispatcher gets control in two ways» Internal: Process does something to relinquish the CPU» External: Interrupts cause dispatcher to take CPU
– On a uniprocessor, can avoid context-switching by:» Avoiding internal events (although virtual memory tricky)» Preventing external events by disabling interrupts
• Consequently, naïve implementation of locks:LockAcquire { disable Ints; }LockRelease { enable Ints; }
• Problems with this approach:– Can’t let user do this! Consider following:
LockAcquire();While(TRUE) {;}
– What happens with I/O or other important events?» “Reactor about to meltdown. Help?”
Synchronization Hardware
• Modern machines provide special atomic hardware
instructions
• Atomic = non-interruptable
• Atomic instructions that allow us either to test and
modify the content of a word or to swap the contents
of two words
Synchronization Hardware
• Test and modify the content of a word atomically
boolean TestAndSet (boolean &target) {
boolean rv = target;
target = true;
return rv;
}
Mutual Exclusion with Test-and-Set
• Shared data:
boolean lock = false;
• Process Pi
do {
while (TestAndSet(lock));
critical section
lock = false;
remainder section
} while (TRUE);
Synchronization Hardware
• Atomically swap two variables
void Swap(boolean &a, boolean &b) {
boolean temp = a;
a = b;
b = temp;
}
Mutual Exclusion with Swap
• Shared data (initialized to false):
boolean lock;
• Process Pi
do {
key = true; // key is a local variable
while (key == true)
Swap(lock,key);
critical section
lock = false;
remainder section
} while (TRUE);
Bounded-waiting Mutual Exclusion with TestandSet()
do {
waiting[i] = TRUE;
key = TRUE;
while (waiting[i] && key)
key = TestAndSet(&lock);
waiting[i] = FALSE;
// critical section
j = (i + 1) % n;
while ((j != i) && !waiting[j])
j = (j + 1) % n;
if (j == i)
lock = FALSE;
else
waiting[j] = FALSE;
// remainder section
} while (TRUE);
Semaphores
• Dijkstra’s work on semaphores established over 30 years ago the foundation of modern techniques
for accomplishing synchronization
• A semaphore, S, is a integer variable that is changed or tested only by one of the two following
indivisible operations
P(S):
while (S 0) do no-op;
S--;
V(S):
S++;
• In Dijkstra’s original paper, the P operation was an abbreviation for the Dutch word Proberen,
meaning “to test” and the V operation was an abbreviation for the word Verhogen, meaning “to
increment”• Now, P() and V() is normally called wait() and signal()
Critical Section for n Processes
• Shared data:
semaphore mutex; // initially mutex = 1
• Process Pi
do {
P(mutex);
critical section
V(mutex);
remainder section
} while (TRUE);
Semaphore Implementation• Must guarantee that no two processes can execute wait () and signal
() on the same semaphore at the same time• Thus, implementation becomes the critical section problem where
the wait and signal code are placed in the crtical section.– Could now have busy waiting in critical section implementation
• But implementation code is short• Little busy waiting if critical section rarely occupied
• Note that applications may spend lots of time in critical sections and therefore this is not a good solution.
Semaphore Implementation with no Busy waiting
• With each semaphore there is an associated waiting queue. Each entry in a waiting queue has two data items:– value (of type integer)– pointer to next record in the list
typedef struct {
int value;
struct process *L;
} semaphore;
• Two operations:– block – place the process invoking the operation on the appropriate
waiting queue.– wakeup – remove one of processes in the waiting queue and place it
in the ready queue.
Semaphore Implementation
• Semaphore operations now defined as
P(S):
S.value--;
if (S.value < 0) {
add this process to S.L;
block();
}
V(S):
S.value++;
if (S.value <= 0) {
remove a process Pi from S.L;
wakeup(Pi);
}
Semaphore as a General Synchronization Tool
• Execute <B> in Pj only after <A> executed in Pi
• Use semaphore flag initialized to 0
Pi Pj
<A> P(flag)
V(flag) <B>
Deadlock and Starvation
• Deadlock – two or more processes are waiting indefinitely for an event that can be caused by only one of the waiting processes
• Starvation – indefinite blocking. A process may never be removed from the semaphore queue in which it is suspended
• Let S and Q be two semaphores initialized to 1
Pi Pj
P(S) P(Q)
P(Q) P(S)
V(S) V(Q)
V(Q) V(S)