process synchronization: conclusion cos 431. event counters alternative producer/consumer solution...
TRANSCRIPT
Event Counters
• Alternative producer/consumer solution
• No need for mutex semaphore
• Example events: Adding/removing item
• Idea: count events
• Processes wait for some number of events
• Atomicity: requires kernel support
Event Counter Example
typedef int event_counter;
event_counter in = 0;event_counter out = 0;
void producer() {int item, num_items = 0;while (TRUE) {
item = produce();num_items++;/* wait for num out >= (num produced
- buffer size) -- that is, until there’s room in the buffer */
await(out,num_items - NUM_SLOTS);enter_item(item,buffer);advance(&in); /* tell consumer something
has arrived */}
}
void consumer () {int item, num_items = 0;while (TRUE) {
num_items++;
/* wait till buffer holds more items than we’d like to
consume */await(in,num_items);item = remove_item(buffer);advance(&out);consume_item(item);
}}
var buffer: shared record... end;
/* consumer */region buffer when count < n do
begin/* add to buffer */
end;
/* consumer */region buffer when count > 0 do
begin/* take from buffer */
end;
• Problems: Cheating Bugs
• Solution: programming language support for critical region
• Region construct
region var when cond do stmt;
Programming Language Support
Monitors
• Monitor: programming language construct for process synch.
• Object-like semantics: procedures and variables are inside of monitors
• Only one process can be active within a monitor at any given time
Example
monitor COS431;integer students[0..MAX];last := 0;
procedure register(student);…
end;
procedure drop(student);…
end;end;
Potential problem:
• suppose we have bounded buffer problem• need mutex...• ...but also need way for process to wait until buffer
is not full/empty• yet if do this inside of monitor (naively), then
deadlock
Solution: Condition Variables
• Condition variables used for interprocess communication
• Process can’t proceed => wait on a condition => not “active”
• Allows another process to enter monitor
• When resource free, active process uses signal on condition variable
• Original process awakens
Monitor Condition Variables
• Possible problem: both signaler and ex-sleeper active simultaneously!
• Solution: make process that signaled exit immediately from the monitor
• Can we do this??
• If several processes are waiting on a condition variable, pick one
• Problem with condition variables: they aren’t counters ==> can’t buffer “signals” like a semaphore does -- signals can be lost
• solution: use state variables for each process
• if P1 wants to signal P2, but P2 not waiting - don’t signal
• Can’t get deadlock with monitors -- sleep and wakeup done only within monitor, and only one active process allowed at a time
• What if other process forgets to signal?
Example of Monitors: Bounded Buffer
monitor COS431;integer students[0..MAX];condition full;last := 0;
procedure register(student);if last = MAX then wait(full);last := last + 1;students[last] := student;
end;
procedure drop(student);remove(student,students);last := last - 1;signal(full);
end;
P1:COS431.register(“Paul”);...last now equals max...COS431.register(“Mary”); ==> blocks
P2:COS431.drop(“Jim”);
...P1 wakes up, finishes adding
Problem with Monitors
• Requires programming language support• C, Pascal, C++, Java – don’t have this• Concurrent Euclid does, but...• Same problems for regions
Problems with Synchronization Mechanisms
• Semaphores: low-level cheating don’t work without shared memory--e.g., when
processes are on different machines• Signals:
only boolean information can be passed only on same machine cheating
• Monitors: need programming language support what if don’t have that language, or need multiple
languages?
Message Passing
• New abstraction/metaphor: process sends a message... ...another receives it later
• Same or different machines
• System calls -- e.g.: send(dest,&msg); receive(dest,&msg); Not what they’re called in Unix (mq_send, e.g.)
• Blocking: blocking receives blocking or non-blocking sends
Solving the Registrar Problem with Messages
Registrar Server
void main() {while (TRUE) {
receive(source,&msg);parse(msg);if (full(class)) {
send(source, “sorry”);} else {
add(student,class);send(source, “okay”);
}}
}
Client Program
void add(student,class) { create_msg(&msg,student,class);
send(registrar,&msg);receive(registrar,&msg);report_results(&msg);
}
Producer/Consumer with Messages
Producervoid main() {
while (TRUE) {produce(&item);
receive(consumer,&msg);pack(item,&msg);send(consumer,&msg);
}}
Consumervoid main() {
// use messages instead of buffer for (i = 0; i < MAX; i++) {
send(producer,&msg);}while (TRUE) {
receive(producer,&msg);item = extract_item(&msg);// send back “empty”send(producer,&msg);process_item(item);
}}
Producer/ConsumerReceiving from Anyone
lpr Program, Machine A
void main(int ARGC, char *ARGV[]) {
process_args(&filename);pack(&msg,filename);send(lpd, &msg);// wait for replyreceive(lpd,&msg);
}
lpd Daemon, Machine B
void main() {while (TRUE) { receive(&any,&msg); unpack(&msg,&filename); send(any,&msg); print_file(filename);}
}
Message Passing
• Messages -- usually buffered until needed in kernel
• Can implement such that sends block rendezvous: synchronize two processes
• Problems when processes on different machines: Lost messages
• acknowledge messages
• ack lost: timeout, retransmit
• receiver must be able to handle redundant, out-of-order msgs Addressing
• same machines: PIDs unique
• different machines: hostname, PIDs may not be unique
• use PID + Internet address:
Equivalence of Primitives
• Can implement one kind of synchronization primitive with another
• E.g.: implement monitors with semaphores Need mutex -- govern access to monitor Need semaphore for each condition variable Compiler help: insert proper system calls where enter/exit monitor
Process 1enter_monitor: Down(mutex);work (in monitor)workexit_monitor: Up(mutex);work (out of monitor)enter_monitor: Down(mutex);blockedwake up, work in monitorwork (in monitor)exit_signal(cvar1): Up(cvar1);work (out of monitor)
Process 2workenter_monitor: Down(mutex)blockedblockedwake up, enter monitorwork (in monitor)wait(cvar1): Up(mutex),Down(cvar1)blockedblockedwake upwork (in monitor)exit_monitor: Up(mutex)
Semaphores Implemented via Messages
• E.g., for distributed process synchronization
• Need a synchronization process
Down(s) {put specifier for Down,
s in msg;send(syncProc,&msg);receive(syncProc,&msg);
}
Up(s) {put specifier for Up,
s in msg;send(syncProc,&msg);receive(syncProc,&msg);
}
syncProc() {semaphore semArray[N];receive(&any,&msg);parse into Up/Down, s;if (Up) {
semArray[s].count++;if (processes waiting on s) {
remove process p from queue;send(p,&msg);
}send(&any,&msg);
} else {semArray[s].count--;if (semaphore[s].count < 0) {
put s on queue; /* implicit block - no msg */
} else {send(&any,msg);
}}
Classical Problems: Dining Philosophers
• Five philosophers• Two states: eating or thinking• Fork between each pair• Each needs two forks to eat (spaghetti, say; or
chopsticks rather than forks)• Problem: assure no deadlocks, no starvation
Dining Philosophers: Simple “Solution”
• Use one semaphore per fork
while (TRUE) {think();Down(left_fork);Down(right_fork);eat();Up(right_fork);Up(left_fork);
}
• Problem?
• Can we check fork first?
• Can we use mutex semaphore?
Dining Philosophers: Solution
• Use state array (eating, thinking, hungry)
• Also semaphore per philosopher
• Philosopher’s semaphore set to 0 initially
void main() {while (TRUE) {
think();take_forks(i);eat(); // eatingput_forks(i);
}
take_forks(i) {Down(mutex);state[i] = HUNGRY;test(i); // forks free?Up(mutex);Down(semaphore[i]);
}
test(i) {if ((state[i] == HUNGRY) &&
(state[right(i)] != EATING) &&(state[left(i)] != EATING)) {
state[i] = EATING; Up(semaphore[i]);}
put_forks(i) {Down(mutex);state[i] = THINKING;test(left(i));test(right(i));Up(mutex);
}
Readers/Writers
• Often: multiple readers multiple writers ex: databases, ATMs, etc.
• Any problem with: multiple readers? multiple writers? readers & writers simultaneously?
• How to synchronize, so that: readers and writers respect shared data and maximum utilization of shared resource?
• Solution uses two semaphores, mutex and wrt
Readers/Writers:Solution?
writer:Down(mutex);Down(wrt);...write...Up(wrt);Up(mutex);
end;
reader:Down(mutex);readers++;if (readers == 1)
Down(wrt);Up(mutex);...read...Down(mutex);readers--;if (readers == 0)
Up(wrt);Up(mutex);
end;
A Readers/Writers Solution
void reader() {while (TRUE) { down(mutex); rc++; // reader count
if (rc == 1)down(wrt);
up(mutex); ...read data... down(mutex); rc--; if (rc == 0)
up(wrt); up(mutex); ...use data...
}}
void writer () {while (TRUE) { ...produce data... down(wrt); ...write data... up(wrt);}
}