25
Process Synchronization: Conclusion COS 431

Process Synchronization: Conclusion COS 431. Event Counters Alternative producer/consumer solution No need for mutex semaphore Example events: Adding/removing

Embed Size (px)

Citation preview

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

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:

[email protected]

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

}