Synchronization

21
Synchronization

description

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. Programs. - PowerPoint PPT Presentation

Transcript of Synchronization

Page 1: Synchronization

Synchronization

Page 2: 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

Page 3: Synchronization

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

Page 4: Synchronization

Synchronization

do {

[acquire lock]

critical section

[release lock]

remainder section

} while (TRUE);

Page 5: Synchronization

“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();

Page 6: Synchronization

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

Page 7: Synchronization

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

Page 8: Synchronization

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?”

Page 9: Synchronization

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

Page 10: Synchronization

Synchronization Hardware

• Test and modify the content of a word atomically

boolean TestAndSet (boolean &target) {

boolean rv = target;

target = true;

return rv;

}

Page 11: Synchronization

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);

Page 12: Synchronization

Synchronization Hardware

• Atomically swap two variables

void Swap(boolean &a, boolean &b) {

boolean temp = a;

a = b;

b = temp;

}

Page 13: Synchronization

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);

Page 14: Synchronization

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);

Page 15: Synchronization

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()

Page 16: Synchronization

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);

Page 17: Synchronization

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.

Page 18: Synchronization

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.

Page 19: Synchronization

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);

}

Page 20: Synchronization

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>

Page 21: Synchronization

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)