HIGHLEVEL SYNCHRONIZATION 1 Large Amount of Work to

  • Slides: 66
Download presentation
HIGH-LEVEL SYNCHRONIZATION 1

HIGH-LEVEL SYNCHRONIZATION 1

Large Amount of Work to Do 2

Large Amount of Work to Do 2

Partition the Work 3

Partition the Work 3

Define Dependencies 1 B 1 A 2 3 A 3 B 4

Define Dependencies 1 B 1 A 2 3 A 3 B 4

Assign Work to Threads Thread 1 A 1 B 2 3 A 3 B

Assign Work to Threads Thread 1 A 1 B 2 3 A 3 B 5

Synchronize Thread Execution Thread 1 A Synchronization Mechanism 1 B 2 3 A 3

Synchronize Thread Execution Thread 1 A Synchronization Mechanism 1 B 2 3 A 3 B 6

The Dining Philosophers Quiz: Write a synchronization schema for the problem 7

The Dining Philosophers Quiz: Write a synchronization schema for the problem 7

First Try Solution philosopher(int i) { while(TRUE) { // Think // Eat P(fork[i]); P(fork[(i+1)

First Try Solution philosopher(int i) { while(TRUE) { // Think // Eat P(fork[i]); P(fork[(i+1) mod 5]); eat(); V(fork[(i+1) mod 5]); V(fork[i]); } } semaphore fork[5] = (1, 1, 1); fork(philosopher, 1, 0); fork(philosopher, 1, 1); fork(philosopher, 1, 2); fork(philosopher, 1, 3); fork(philosopher, 1, 4); 8

Possible remedies to deadlock � � � Allow at most four philosophers to be

Possible remedies to deadlock � � � Allow at most four philosophers to be sitting simultaneously at the table Allow a philosopher to pick up chopsticks only if both chopsticks are available Use an asymmetric solution – an odd philosopher picks up first the left then the right chopstick; an even philosopher picks up the right then the left 9

Remedy three philosopher(int i) { while(TRUE) { // Think // Eat j = i

Remedy three philosopher(int i) { while(TRUE) { // Think // Eat j = i % 2; P(fork[(i+j) mod 5]); P(fork[(i+1 -j) mod 5]); eat(); V(fork[(i+1 -j) mod 5]); V(fork[[(i+j) mod 5]); } } semaphore fork[5] = (1, 1, 1); fork(philosopher, 1, 0); fork(philosopher, 1, 1); fork(philosopher, 1, 2); fork(philosopher, 1, 3); fork(philosopher, 1, 4); 10

Still problems � � These remedies will avoid deadlock Unfortunately, they do not avoid

Still problems � � These remedies will avoid deadlock Unfortunately, they do not avoid starvation Thus they are not satisfactory Another problem arises when semaphores are nested 11

Nesting Semaphore Operations pr P Operation Order ps P Operation Order P(mutex 1); P(mutex

Nesting Semaphore Operations pr P Operation Order ps P Operation Order P(mutex 1); P(mutex 2); P(mutex 1); <access R 1>; <access R 2>; V(mutex 2); V(mutex 1); V(mutex 2); (a) (b) What happens if pr attempts to obtain mutex 1 before mutex 2, while ps tries to obtain mutex 2 before mutex 1? DEADLOCK 12

Simultaneous Semaphores � The orders of P operations on semaphores are critical � Otherwise

Simultaneous Semaphores � The orders of P operations on semaphores are critical � Otherwise � deadlocks are possible Simultaneous semaphores � Psimultaneous(S 1, . . , Sn) � The process gets all the semaphores or none of them 13

Abstracting Semaphores � � As we have seen, relatively simple problems, such as the

Abstracting Semaphores � � As we have seen, relatively simple problems, such as the dining philosophers problem, can be very difficult to solve Look for abstractions to simplify solutions � AND synchronization � Events � Monitors � … there are others. . . 14

AND Synchronization � � Given two resources, R 1 and R 2 Some processes

AND Synchronization � � Given two resources, R 1 and R 2 Some processes access R 1, some R 2, some both in the same critical section Need to avoid deadlock due to ordering of P operations Psimultaneous(S 1, …, Sn) 15

Simultaneous Semaphores Def P_sim(semaphore S, int N) { L 1: if ((S[0]>=1)&& … &&(S[N-1]>=1))

Simultaneous Semaphores Def P_sim(semaphore S, int N) { L 1: if ((S[0]>=1)&& … &&(S[N-1]>=1)) { for(i=0; i<N; i++) S[i]--; } else { Enqueue the calling thread in the queue for the first S[i] where S[i]<1; The calling thread is blocked while it is in the queue; Goto L 1; // When the thread is removed from the queue } } V_sim(semaphore S, int N) { for(i=0; i<N; i++) { S[i]++; Dequeue all threads in the queue for S[i]; All such threads are now ready to run (but may be blocked again in Psimultaneous); } 16

Simultaneous Semaphore int R_num = 0, S_num = 0; Queue R_wait, S_wait; Semaphore mutex

Simultaneous Semaphore int R_num = 0, S_num = 0; Queue R_wait, S_wait; Semaphore mutex = 1; P_sim(PID calling. Thread, semaphore R, semaphore S) { L 1: P(mutex); if(R. val>0)&&(S. val>0)) { P(R); P(S); V(mutex); } else { if(R. val==0) { V_sim(semaphore R, semaphore S) { R_num++; enqueue(calling. Thread, R_wait); P(mutex); V(R); V(S); V(mutex); if(R_num>0) { goto L 1; R_num--; } else { dequeue(R_wait); // Release a thread S_num++; enqueue(Calling. Thread, S_wait); } if(S_num>0) { V(mutex); S_num--; goto L 1; dequeue(S_wait); // Release a thread } } } V(mutex); } } 17

Simultaneous Semaphore � Implementation is tricky � Requires that a “third party” enqueue the

Simultaneous Semaphore � Implementation is tricky � Requires that a “third party” enqueue the calling thread (the thread cannot enqueue itself, then release a critical section) � The method of implementation determines whether the semaphore is “primitive” � If implemented as on previous slide, then it is an abstraction – can be implemented in library � If implemented in the OS, then it can be considered a primitive 18

Case For n=2 Semaphores semaphore mutex = 1; semaphore block = 0; P. sim(int

Case For n=2 Semaphores semaphore mutex = 1; semaphore block = 0; P. sim(int S, int R) { P(mutex); S--; R--; if((S < 0) || (R < 0)) { V(mutex); P(block); } else V(mutex); } V. sim(int S, int R) { P(mutex); S++; R++; if(((S >= 0) && (R >= 0)) && ((S == 0) || (R == 0))) V(block); V(mutex); } 19

Dining Philosophers Problem philosopher(int i) { while(TRUE) { // Think // Eat Psimultaneous(fork[i], fork

Dining Philosophers Problem philosopher(int i) { while(TRUE) { // Think // Eat Psimultaneous(fork[i], fork [(i+1) mod 5]); eat(); Vsimultaneous(fork[i], fork [(i+1) mod 5]); } } semaphore fork[5] = (1, 1, 1); fork(philosopher, 1, 0); fork(philosopher, 1, 1); fork(philosopher, 1, 2); fork(philosopher, 1, 3); fork(philosopher, 1, 4); 20

Events � � Exact definition is specific to each OS A process can wait

Events � � Exact definition is specific to each OS A process can wait on an event until another process signals the event Have event descriptor (“event control block”) Active approach � Multiple processes can wait on an event � Exactly one process is unblocked when a signal occurs � A signal with no waiting process is ignored � May have a queue function that returns number of processes waiting on the event 21

Example class Event { … public: void signal(); void wait() int queue(); } shared

Example class Event { … public: void signal(); void wait() int queue(); } shared Event top. Of. Hour; . . . // Wait until the top of the hour before proceeding top. Of. Hour. wait(); // It’s the top of the hour. . . 1 wait() 3 Resume top. Of. Hour 2 signal() shared Event top. Of. Hour; . . . while(TRUE) if(is. Top. Of. Hour()) while(top. Of. Hour. queue() > 0) top. Of. Hour. signal(); }. . .

UNIX Signals � A UNIX signal corresponds to an event � It is raised

UNIX Signals � A UNIX signal corresponds to an event � It is raised by one process (or hardware) to call another process’s attention to an event � It can be caught (or ignored) by the subject process � Justification for including signals was for the OS to inform a user process of an event � User pressed delete key � Program tried to divide by zero � Attempt to write to a nonexistent pipe � etc. 23

More on Signals � � � Each version of UNIX has a fixed set

More on Signals � � � Each version of UNIX has a fixed set of signals (Linux has 31 of them) signal. h defines the signals in the OS App programs can use SIGUSR 1 & SIGUSR 2 for arbitrary signaling Raise a signal with kill(pid, signal) Process can let default handler catch the signal, catch the signal with own code, or cause it to be ignored 24

More on Signals (cont) � OS signal system call � To ignore: signal(SIG#, SIG_IGN)

More on Signals (cont) � OS signal system call � To ignore: signal(SIG#, SIG_IGN) � To reinstate default: signal(SIG#, SIG_DFL) � To catch: signal(SIG#, my. Handler) � � Provides a facility for writing your own event handlers in the style of interrupt handlers Note: default action is to kill the process! 25

Signal Handling /* code for process p */. . . signal(SIG#, my. Hndlr); .

Signal Handling /* code for process p */. . . signal(SIG#, my. Hndlr); . . . /* ARBITRARY CODE */ void my. Hndlr(. . . ) { /* ARBITRARY CODE */ } 26

Signal Handling /* code for process p */. . . signal(SIG#, sig_hndlr); . .

Signal Handling /* code for process p */. . . signal(SIG#, sig_hndlr); . . . /* ARBITRARY CODE */ void sig_hndlr(. . . ) { /* ARBITRARY CODE */ } An executing process, q Raise “SIG#” for “p” q is blocked runs in p’s address space sig_hndlr q resumes execution 27

Using UNIX Signals Pi’s Address Space Pi’s Execution Pj’s Execution program Pi’s Signal Handler

Using UNIX Signals Pi’s Address Space Pi’s Execution Pj’s Execution program Pi’s Signal Handler signal hndlr data stack & heap 28

Toy Signal Handler #include <signal. h> static void sig_handler(int); int main () { int

Toy Signal Handler #include <signal. h> static void sig_handler(int); int main () { int i, parent_pid, child_pid, status; if(signal(SIGUSR 1, sig_handler) == SIG_ERR) printf(“Parent: Unable to create handler for SIGUSR 1n”); if(signal(SIGUSR 2, sig_handler) == SIG_ERR) printf(“Parent: Unable to create handler for SIGUSR 2n”); parent_pid = getpid(); if((child_pid = fork()) == 0) { kill(parent_pid, SIGUSR 1); for (; ; ) pause(); } else { kill(child_pid, SIGUSR 2); printf(“Parent: Terminating child … n”); kill(child_pid, SIGTERM); wait(&status); printf(“donen”); } } 29

Toy Signal Handler (2) static void sig_handler(int signo) { switch(signo) { case SIGUSR 1:

Toy Signal Handler (2) static void sig_handler(int signo) { switch(signo) { case SIGUSR 1: /* Incoming SIGUSR 1 */ printf(“Parent: Received SIGUSER 1n”); break; case SIGUSR 2: /* Incoming SIGUSR 2 */ printf(“Child: Received SIGUSER 2n”); break; default: break; } return } 30

Monitors � � Semaphore primitives difficult to use for complex synchronization Monitors derived to

Monitors � � Semaphore primitives difficult to use for complex synchronization Monitors derived to simplify complexity of synchronization problems by abstracting away details Idea credited to C. A. R. Hoare (1974) and Per Brinch Hansen (1977) Construct ensures that only one process can be active at a time in the monitor – no need to code the synchronization constraint explicitly 31

�High-level Monitors synchronization construct that allows the safe sharing of an abstract data type

�High-level Monitors synchronization construct that allows the safe sharing of an abstract data type among concurrent processes. class monitor { variable declarations semaphore mutex = 1; public P 1 : (…) { P(mutex); <processing for P 1> V(mutex); }; . . . . } 32

Monitors – cont. � To allow a process to wait within the monitor, a

Monitors – cont. � To allow a process to wait within the monitor, a condition variable must be declared, as condition x, y; � Condition variable can only be used with the operations wait and signal. � The operation x. wait; means that the process invoking this operation is suspended until another process invokes x. signal; � The x. signal operation resumes exactly one suspended process. If no process is suspended, then the signal operation has no effect. 33

Monitors – cont. 34

Monitors – cont. 34

Example: Shared Balance � � Some processes want to increment; others to decrement Monitor

Example: Shared Balance � � Some processes want to increment; others to decrement Monitor provides functions to change the value but protects access to shared variable as a critical section monitor shared. Balance { double balance; public: credit(double amount) {balance += amount; }; debit(double amount) {balance -= amount; }; . . . }; 35

Example: Readers & Writers monitor reader. Writer_1 { int number. Of. Readers = 0;

Example: Readers & Writers monitor reader. Writer_1 { int number. Of. Readers = 0; int number. Of. Writers = 0; boolean busy = FALSE; public: start. Read() { reader(){ … while(TRUE) { }; . . . finish. Read() { start. Read(); … finish. Read(); }; . . . start. Write() { } … fork(reader, 0); }; . . . finish. Write() { fork(reader, 0): … fork(writer, 0); }; . . . }; fork(writer, 0); writer(){ while(TRUE) { . . . start. Writer(); finish. Writer(); . . . } 36

Example: Readers & Writers monitor reader. Writer_1 { int number. Of. Readers = 0;

Example: Readers & Writers monitor reader. Writer_1 { int number. Of. Readers = 0; int number. Of. Writers = 0; boolean busy = FALSE; public: start. Write() { start. Read() { number. Of. Writers++; while(number. Of. Writers != 0) ; while( number. Of. Readers++; busy || }; (number. Of. Readers > 0) finish. Read() { ) ; number. Of. Readers--; busy = TRUE; }; finish. Write() { number. Of. Writers--; busy = FALSE; }; }; 37

Example: Readers & Writers • OOPS! • Deadlock can happen monitor reader. Writer_1 {

Example: Readers & Writers • OOPS! • Deadlock can happen monitor reader. Writer_1 { int number. Of. Readers = 0; int number. Of. Writers = 0; boolean busy = FALSE; public: start. Write() { start. Read() { number. Of. Writers++; while(number. Of. Writers != 0) ; while( number. Of. Readers++; busy || }; (number. Of. Readers > 0) finish. Read() { ) ; number. Of. Readers--; busy = TRUE; }; finish. Write() { number. Of. Writers--; busy = FALSE; }; }; 38

Sometimes Need to Suspend � � Process obtains monitor, but detects a condition for

Sometimes Need to Suspend � � Process obtains monitor, but detects a condition for which it needs to wait Want special mechanism to suspend until condition is met, then resume � Process that makes condition true must exit monitor � Suspended process then resumes � This is where the Condition Variable comes in 39

Condition Variables � � � Essentially an event (as defined previously) Occurs only inside

Condition Variables � � � Essentially an event (as defined previously) Occurs only inside a monitor Operations to manipulate condition variable Suspend invoking process until another executes a signal � signal: Resume one process if any are suspended, otherwise do nothing � queue: Return TRUE if there is at least one process suspended on the condition variable � wait: 40

Active vs Passive signal � Hoare semantics: same as active semaphore executes signal while

Active vs Passive signal � Hoare semantics: same as active semaphore executes signal while p 1 is waiting p 0 yields the monitor to p 1 � The signal is only TRUE the instant it happens � p 0 � Brinch Hansen (“Mesa”) semantics: same as passive semaphore executes signal while p 1 is waiting p 0 continues to execute, then when p 0 exits the monitor p 1 can receive the signal � Used in the Xerox Mesa implementation � p 0 41

Hoare vs Mesa Semantics � Hoare semantics: . . . if(resource. Not. Available()) resource.

Hoare vs Mesa Semantics � Hoare semantics: . . . if(resource. Not. Available()) resource. Condition. wait(); /* now available … continue … */. . . Mesa semantics: . . . while(resource. Not. Available()) resource. Condition. wait(); /* now available … continue … */. . . 42

2 nd Try at Readers & Writers monitor reader. Writer_2 { int number. Of.

2 nd Try at Readers & Writers monitor reader. Writer_2 { int number. Of. Readers = 0; boolean busy = FALSE; condition ok. To. Read, ok. To. Write; public: start. Write() { start. Read() { if((number. Of. Readers != 0) if(busy || (ok. To. Write. queue()) || busy) ok. To. Read. wait(); ok. To. Write. wait(); number. Of. Readers++; busy = TRUE; ok. To. Read. signal(); }; finish. Write() { finish. Read() { busy = FALSE; number. Of. Readers--; if(ok. To. Read. queue()) if(number. Of. Readers == 0) ok. To. Read. signal() ok. To. Write. signal(); else }; ok. To. Write. signal() }; }; 43

Example: Synchronizing Traffic � � � One-way tunnel Can only use tunnel if no

Example: Synchronizing Traffic � � � One-way tunnel Can only use tunnel if no oncoming traffic OK to use tunnel if traffic is already flowing the right way 44

Example: Synchronizing Traffic monitor tunnel { int northbound = 0, southbound = 0; traffic.

Example: Synchronizing Traffic monitor tunnel { int northbound = 0, southbound = 0; traffic. Signal nb. Signal = RED, sb. Signal = GREEN; condition busy; public: nb. Arrival() { if(southbound > 0) busy. wait(); northbound++; nb. Signal = GREEN; sb. Signal = RED; }; sb. Arrival() { if(northbound > 0) busy. wait(); southbound++; nb. Signal = RED; sb. Signal = GREEN; }; depart(Direction exit) ( if(exit = NORTH { northbound--; if(northbound == 0) while(busy. queue()) busy. signal(); else if(exit == SOUTH) { southbound--; if(southbound == 0) while(busy. queue()) busy. signal(); } } }

Dining Philosophers … again. . . #define N # enum status(EATING, HUNGRY, THINKING}; monitor

Dining Philosophers … again. . . #define N # enum status(EATING, HUNGRY, THINKING}; monitor dining. Philosophers { status state[N]; condition self[N]; test(int i) { if((state[(i-1) mod N] != EATING) && (state[i] == HUNGRY) && (state[(i+1) mod N] != EATING)) { state[i] = EATING; self[i]. signal(); } }; public: dining. Philosophers() { // Initilization for(int i = 0; i < N; i++) state[i] = THINKING; }; 46

Dining Philosophers … again. . . test(int i) { if((state[(i-1) mod N] != EATING)

Dining Philosophers … again. . . test(int i) { if((state[(i-1) mod N] != EATING) && (state[i] == HUNGRY) && (state[(i+1) mod N] != EATING)) { state[i] = EATING; self[i]. signal(); }; public: dining. Philosophers() {. . . }; pick. Up. Forks(int i) { state[i] = HUNGRY; test(i); if(state[i] != EATING) self[i]. wait(); }; put. Down. Forks(int i) { state[i] = THINKING; test((i-1) mod N); test((i+1) mod N); } 47

Experience with Monitors � � Danger of deadlock with nested calls Monitors were implemented

Experience with Monitors � � Danger of deadlock with nested calls Monitors were implemented in Mesa � Used Brinch Hansen semantics � Nested monitor calls are, in fact, a problem � Difficult to get the right behavior with these semantics � Needed timeouts, aborts, etc. 48

Interprocess Communication (IPC) � � � Signals, semaphores, etc. do not pass information from

Interprocess Communication (IPC) � � � Signals, semaphores, etc. do not pass information from one process to another Monitors support information sharing, but only through shared memory in the monitor There may be no shared memory � OS does not support it � Processes are on different machines on a network � Can use messages to pass info while synchronizing 49

Communication through Messages � Messages and message queues: � The most common method �

Communication through Messages � Messages and message queues: � The most common method � Send messages to message queues � Receive messages from message queues � Message system � processes communicate with each other without resorting to shared variables 50

IPC Mechanisms Info to be shared Address Space for p 0 Message OS IPC

IPC Mechanisms Info to be shared Address Space for p 0 Message OS IPC Mechanism Info copy Address Space for p 1 • Must bypass memory protection mechanism for local copies • Must be able to use a network for remote copies 51

Refined IPC Mechanism • Avoid these spontaneous changes to p 1’s address space through

Refined IPC Mechanism • Avoid these spontaneous changes to p 1’s address space through the use of mailboxes Address Space for p 0 Address Space for p 1 Mailbox for p 1 Info to be shared Message send(… p 1, …); Info copy receive(…); OS Interface send function receive function 52

Refined IPC Mechanism • Avoid these spontaneous changes to p 1’s address space through

Refined IPC Mechanism • Avoid these spontaneous changes to p 1’s address space through the use of mailboxes Address Space for p 0 But the process could accidentally destroy them! Address Space for p 1 Mailbox for p 1 Info to be shared Message send(… p 1, …); Info copy receive(…); OS Interface send function receive function 53

Refined IPC Mechanism • OS manages the mailbox space • More secure message system

Refined IPC Mechanism • OS manages the mailbox space • More secure message system Address Space for p 0 Address Space for p 1 Info to be shared Info copy send(… p 1, …); receive(…); OS Interface Mailbox for p 1 send function Message receive function 54

Message Protocols � Sender transmits a set of bits to receiver � How does

Message Protocols � Sender transmits a set of bits to receiver � How does the sender know when the receiver is ready (or when the receiver obtained the info)? � How does the receiver know how to interpret the info? � Need a protocol for communication Standard “envelope” for containing the info Standard header � A message system specifies the protocols 55

Transmit Operations � Asynchronous send: � Delivers message to receiver’s mailbox � Continues execution

Transmit Operations � Asynchronous send: � Delivers message to receiver’s mailbox � Continues execution � No feedback on when (or if) info was delivered � Synchronous send: � Goal is to block sender until message is received by a process Variant sometimes used in networks: Until the message is in the mailbox 56

Receive Operation � Blocking receive: � Return the first message in the mailbox �

Receive Operation � Blocking receive: � Return the first message in the mailbox � If there is no message in mailbox, block the receiver until one arrives � Nonblocking receive: � Return the first message in the mailbox � If there is no message in mailbox, return with an indication to that effect 57

Synchronized IPC Code for p 1 Code for p 2 /* signal p 2

Synchronized IPC Code for p 1 Code for p 2 /* signal p 2 */ sync. Send(message 1, p 2); <waiting …>; /* wait for signal from p 2 */ block. Receive(msg. Buff, &from); sync. Send(…) block. Receive(…) /* wait for signal from p 1 */ block. Receive(msg. Buff, &from); <process message>; /* signal p 1 */ sync. Send(message 2, p 1); block. Receive(…) sync. Send(…) 58

Asynchronous IPC Code for p 1 Code for p 2 /* signal p 2

Asynchronous IPC Code for p 1 Code for p 2 /* signal p 2 */ async. Send(message 1, p 2); <other processing>; /* wait for signal from p 2 */ while(!nb. Receive(&msg, &from)); /* test for signal from p 1 */ if(nb. Receive(&msg, &from)) { <process message>; async. Send(message 2, p 1); }else< <other processing>; } nonblock. Receive(…) async. Send(…) nonblock. Receive(…) async. Send(…) 59

UNIX Pipes Address Space for p 1 Info to be shared Info copy write(pipe[1],

UNIX Pipes Address Space for p 1 Info to be shared Info copy write(pipe[1], …); System Call Interface read(pipe[0]); pipe for p 1 and p 2 write function read function 60

UNIX Pipes � (cont) The pipe interface is intended to look like a file

UNIX Pipes � (cont) The pipe interface is intended to look like a file interface � Analog of open is to create the pipe � File read/write system calls are used to send/receive information on the pipe � What is going on here? � Kernel creates a buffer when pipe is created � Processes can read/write into/out of their address spaces from/to the buffer � Processes just need a handle to the buffer 61

UNIX Pipes (cont) File handles are copied on fork … so are pipe handles

UNIX Pipes (cont) File handles are copied on fork … so are pipe handles int pipe. ID[2]; . . . pipe(pipe. ID); . . . if(fork() == 0) { /* the child */. . . read(pipe. ID[0], child. Buf, len); <process the message>; . . . } else { /* the parent */. . . write(pipe. ID[1], msg. To. Child, len); . . . } 62

UNIX Pipes � � � (cont) The normal write is an asynchronous op (that

UNIX Pipes � � � (cont) The normal write is an asynchronous op (that notifies of write errors) The normal read is a blocking read The read operation can be nonblocking #include <sys/ioctl. h>. . . int pipe. ID[2]; . . . pipe(pipe. ID); ioctl(pipe. ID[0], FIONBIO, &on); . . . read(pipe. ID[0], buffer, len); if(errno != EWOULDBLOCK) { /* no data */ } else { /* have data */ 63

Explicit Event Ordering � � Alternative technique of growing importance in network systems Rely

Explicit Event Ordering � � Alternative technique of growing importance in network systems Rely on knowing the relative order of occurrence of every event � (occurrence of y in pj) < (occurrence of x in pi) � Then can synchronize by explicitly specifying each relation (when it is important) advance(event. Count): Announces the occurrence an event related to event. Count, causing it to be of incremented by 1 await(event. Count, v): long as event. Count < v. Causes process to block as 64

Bounded Buffer using Precedence consumer() { producer() { int i = 1; while(TRUE) {

Bounded Buffer using Precedence consumer() { producer() { int i = 1; while(TRUE) { await(in, i); await(out, i-N); produce(buffer[(i-1)mod N]); consume(buffer[(i-1)mod N]); advance(out); advance(in); i++; } } } } eventcount in = 0; out = 0; fork(producer, 0); fork(consumer, 0); 65

More on Event. Counts � � � Notice that advance and await need not

More on Event. Counts � � � Notice that advance and await need not be uninterruptible There is no requirement for shared memory For full use of this mechanism, actually need to extend it a bit with a sequencer Underlying theory is also used to implement “virtual global clocks” in a network Emerging as a preferred synchronization mechanism on networks 66