Cs414 Fa05 06 Semaphores
Cs414 Fa05 06 Semaphores
Cs414 Fa05 06 Semaphores
Out In
In Out
1
Producer-Consumer Problem Producer-Consumer Problem
Shared: Semaphores mutex, empty, full;
First attempt to solve: Shared: int counter;
any_t buffer[N]; Init: mutex = 1; /* for mutual exclusion*/
Producer empty = N; /* number empty bufs */
Init: counter = 0; full = 0; /* number full bufs */
while (true) { Producer Consumer
/* produce an item in nextProduced*/
while (counter == N) do { do {
; /* do nothing */ ... P(full);
buffer[in] = nextProduced; Consumer // produce an item in nextp P(mutex);
in = (in + 1) % N; ... ...
counter++; while (true) { P(empty); // remove item to nextc
} while (counter == 0) P(mutex); ...
; /* do nothing */ ... V(mutex);
nextConsumed = buffer[out]; // add nextp to buffer V(empty);
out = (out + 1) % N; ... ...
counter--; V(mutex); // consume item in nextc
/* consume an item in nextConsumed*/ V(full); ...
} } while (true); } while (true);
2
Dining Philosopher’s Problem A non-solution
# define N 5
• Dijkstra
V(fork[i]);
V(fork[i+1]);
/* think */
} while(true);
Philosopher i
take_fork(i) {
P(mutex);
Language Support for
do {
state[i] = hungry;
test(i);
test(i) {
if(state[i] == hungry
Concurrency
take_fork(i); V(mutex);
&& state[(i+1)%N] != eating
/* eat */ P(s[i]);
&& state[(i-1+N)%N != eating)
put_fork(i); }
{
/* think */ state[i] = eating;
} while(true); put_fork(i) {
V(s[i]);
P(mutex);
}
state[i] = thinking;
test((i+1)%N);
test((i-1+N)%N);
V(mutex);
}
3
Common programming errors What’s wrong?
Shared: Semaphores mutex, empty, full;
4
Structure of a Monitor
Monitor monitor_name
Synchronization Using Monitors
{ For example:
// shared variable declarations • Defines Condition Variables:
Monitor stack
procedure P1(. . . .) { {
– condition x;
.... int top; – Provides a mechanism to wait for events
} void push(any_t *) { • Resources available, any writers
.... • 3 atomic operations on Condition Variables
procedure P2(. . . .) {
}
.... – x.wait(): release monitor lock, sleep until woken up
} any_t * pop() { ⇒ condition variables have waiting queues too
. ....
. – x.notify(): wake one process waiting on condition (if there is one)
}
procedure PN(. . . .) { • No history associated with signal
.... initialization_code() { – x.broadcast(): wake all processes waiting on condition
} .... • Useful for resource manager
}
initialization_code(. . . .) {
}
• Condition variables are not Boolean
.... – If(x) then { } does not make sense
} only one instance of stack can
} be modified at a time
5
Mesa-style monitor subtleties Mesa-style subtleties
char buf[N]; // producer/consumer with monitors
char buf[N]; // producer/consumer with monitors int n = 0, tail = 0, head = 0;
int n = 0, tail = 0, head = 0; condition not_empty, not_full;
condition not_empty, not_full; Consider the following time line: void put(char ch)
void put(char ch)
0. initial condition: n = 0 while(n == N)
if(n == N)
wait(not_full);
1. c0 tries to take char, blocks wait(not_full);
buf[head] = ch;
When can we replace
buf[head%N] = ch; on not_empty (releasing monitor
lock) head = (head+1)%N; “while” with “if”?
head++;
n++; 2. p0 puts a char (n = 1), signals n++;
signal(not_empty); not_empty signal(not_full);
char get() 3. c0 is put on run queue char get()
if(n == 0) 4. Before c0 runs, another while(n == 0)
wait(not_empty); consumer thread c1 enters wait(not_empty);
ch = buf[tail%N]; ch = buf[tail];
and takes character (n = 0)
tail++; tail = (tail+1) % N;
n--;
5. c0 runs.
n--;
signal(not_full); signal(not_full);
return ch; Possible fixes? return ch;
6
Optimistic Concurrency Control
• Example: hits = hits + 1;
A) Read hits into register R1
B) Add 1 to R1 and store it in R2
C) Atomically store R2 in hits only if hits==R1 (i.e. CAS)
• If store didn’t write goto A
• Can be extended to any data structure:
A) Make copy of data structure, modify copy.
B) Use atomic word compare-and-swap to update pointer.
C) Goto A if some other thread beat you to the update.
Less overhead, deals with failures better
Lots of retrying under heavy load