Atomic Read-Modify-Write: Lock: If (!location) If (!test-And-Set (Location) ) Return Goto Lock
Atomic Read-Modify-Write: Lock: If (!location) If (!test-And-Set (Location) ) Return Goto Lock
lock value = 0;
Arvind Krishnamurthy
Spring 2001 Lock::Acquire() { while (test&set(value) == 1); }
Lock:
Lock:
next_ticket = fetch_and_increment(next_ticket)
my_slot = fetch_and_increment(next_slot);
while (next_ticket != now_serving); if (my_place % numProcs == 0)
fetch_and_add(next_slot, -numProcss);
Unlock: my_slot = my_slot % numProcs;
now_serving++; while (slots[my_slot] == must_wait);
slots[my_slot] = must_wait;
n Ensures fairness
Unlock:
n Still could result in a lot of bus transactions (every processor caches slots[(my_slot + 1) % numProcs] = has_lock;
now_serving); each release results in P invalidations and P loads
1
Summary The big picture
Many threads + sharing state = race conditions
Concurrent Applications
n
n one thread modifying while others reading/writing
General solution is to use high-level primitives, e.g., locks. It lets us Load/Store Interrupt disable Test&Set
Atomic Ops
n
n What is next?
n Synchronization is a way of coordinating multiple
n definitions of semaphores, monitors, and condition variables concurrent activities that are using shared state.
n how to implement them?
how to use them to solve synchronization problems?
What are the right synchronization abstractions, to make it
n
n
easy to build concurrent programs ?
n Future:
n message-passing and IPC n Locks, semaphores, condition variables, and monitors are
n language support to concurrency just various ways of structuring the sharing.
2
How to use semaphores Scheduling constaints
n Binary semaphores can be used for mutual exclusion: n Something must happen after one another
initial value of 1; P() is called before the critical section; and V() is called after the
critical section.
semaphore->P(); Initial value of semaphore = 0;
// critical section goes here Fork a child thread
semaphore->V(); Thread::Join calls P // will wait until something
// makes the semaphore positive
-------------------------------------------------------------------------
n Scheduling constraints
n having one thread to wait for something to happen Thread finish calls V // makes the semaphore positive
n Example: Thread::Join, which must wait for a thread to terminate. By
// and wakes up the thread
setting the initial value to 0 instead of 1, we can implement waiting on
a semaphore // waiting in Join
Example: producer-consumer
Scheduling with Semaphores with a bounded buffer
n In general, scheduling dependencies between threads T1, T2,
…, Tn can be enforced with n-1 semaphores, S1, S2, …, Sn-1 n Example:
used as follows: n cpp file.S | as
n T1 runs and signals V(S1) when done.
n Tm waits on Sm-1 (using P) and signals V(Sm) when done.
n (contrived) example: schedule print(f(x,y))
float x, y, z;
sem Sx = 0, Sy = 0, Sz = 0; Producer Consumer
T1: T2: T3:
x = …; P(Sx); P(Sz);
V(Sx); P(Sy); print(z);
y = …; z = f(x,y); …
V(Sy); V(Sz);
… ...
3
Producer-consumer with
semaphores (2) Order of P&Vs
Semaphore fullBuffers = 0; // initially no coke Semaphore fullBuffers = 0; // initially no coke
Semaphore emptyBuffers = numBuffers; Semaphore emptyBuffers = numBuffers;
// initially, # of empty slots semaphore used to Consumer() { // initially, # of empty slots semaphore used to Consumer() {
// count how many resources there are fullBuffers.P(); // check if there is // count how many resources there are mutex.P(); // make sure no one
Semaphore mutex = 1; // no one using the machine // a coke in the machine Semaphore mutex = 1; // no one using the machine // else is using machin
mutex.P(); // make sure no one fullBuffers.P(); // check if there is
Producer() { // else is using machine Producer() { // a coke in the machine
emptyBuffers.P(); // check if there is space mutex.P(); // make sure no one else
// for more coke take 1 Coke out; // is using machine take 1 Coke out;
mutex.P(); // make sure no one else emptyBuffers.P(); // check if there is space
// is using machine mutex.V(); // next person’s turn // for more coke fullBuffers.V(); // tell producer
fullBuffers.V(); // tell producer // we need more
put 1 Coke in machine; // we need more put 1 Coke in machine; mutex.V(); // next person’s turn
} }
mutex.V(); // ok for others to use machine fullBuffers.V(); // tell consumers there is now
fullBuffers.V(); // tell consumers there is now // a Coke in the machine
Deadlock---two or more processes are
// a Coke in the machine What if we have 2 producers and 2 mutex.V(); // ok for others to use machine
waiting indefinitely for an event that
} consumers? }
can be caused by only one of the
waiting processes.