250 likes | 351 Views
Process Synchronization: Conclusion. COS 431. 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.
E N D
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 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); } } 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 */ } }
Programming Language Support • Problems: • Cheating • Bugs • Solution: programming language support for critical region • Region construct region var when cond do stmt; 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;
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
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); } Solving the Registrar Problem with Messages
Producer void main() { while (TRUE) { produce(&item); receive(consumer,&msg); pack(item,&msg); send(consumer,&msg); } } Consumer void 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/Consumer with Messages
lpr Program, Machine A void main(int ARGC, char *ARGV[]) { process_args(&filename); pack(&msg,filename); send(lpd, &msg); // wait for reply receive(lpd,&msg); } lpd Daemon, Machine B void main() { while (TRUE) { receive(&any,&msg); unpack(&msg,&filename); send(any,&msg); print_file(filename); } } Producer/ConsumerReceiving from Anyone
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: 2976@umcs.maine.edu
Process 1 enter_monitor: Down(mutex); work (in monitor) work exit_monitor: Up(mutex); work (out of monitor) enter_monitor: Down(mutex); blocked wake up, work in monitor work (in monitor) exit_signal(cvar1): Up(cvar1); work (out of monitor) Process 2 work enter_monitor: Down(mutex) blocked blocked wake up, enter monitor work (in monitor) wait(cvar1): Up(mutex),Down(cvar1) blocked blocked wake up work (in monitor) exit_monitor: Up(mutex) 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
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); } } 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); } Semaphores Implemented via Messages • E.g., for distributed process synchronization • Need a synchronization process
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(); // eating put_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
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; 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); } } A Readers/Writers Solution