High-Level Synchronization

Slides:



Advertisements
Similar presentations
Operating Systems: Monitors 1 Monitors (C.A.R. Hoare) higher level construct than semaphores a package of grouped procedures, variables and data i.e. object.
Advertisements

More on Processes Chapter 3. Process image _the physical representation of a process in the OS _an address space consisting of code, data and stack segments.
Chapter 6: Process Synchronization
02/27/2004CSCI 315 Operating Systems Design1 Process Synchronization Deadlock Notice: The slides for this lecture have been largely based on those accompanying.
A 1B 2 2 3A 3B 5 1A 1B Thread 2 2 3A 3B.
6.5 Semaphore Can only be accessed via two indivisible (atomic) operations wait (S) { while S
Monitors Chapter 7. The semaphore is a low-level primitive because it is unstructured. If we were to build a large system using semaphores alone, the.
Informationsteknologi Wednesday, September 26, 2007 Computer Systems/Operating Systems - Class 91 Today’s class Mutual exclusion and synchronization 
1 Concurrency: Mutual Exclusion and Synchronization Chapter 5.
02/19/2010CSCI 315 Operating Systems Design1 Process Synchronization Notice: The slides for this lecture have been largely based on those accompanying.
Concurrency: Deadlock and Starvation Chapter 6. Revision Describe three necessary conditions for deadlock Which condition is the result of the three necessary.
High-Level Synchronization. Dining Philosophers while(TRUE) { think(); eat(); } Quiz: Write a synchronization schema for the problem.
Inter Process Communication:  It is an essential aspect of process management. By allowing processes to communicate with each other: 1.We can synchronize.
Concurrency: Mutual Exclusion, Synchronization, Deadlock, and Starvation in Representative Operating Systems.
Dining Philosophers, Monitors, and Condition Variables
IPC and Classical Synchronization Problems
1 Concurrency: Deadlock and Starvation Chapter 6.
02/25/2004CSCI 315 Operating Systems Design1 Process Synchronization Notice: The slides for this lecture have been largely based on those accompanying.
Chapter 6 Concurrency: Deadlock and Starvation Operating Systems: Internals and Design Principles, 6/E William Stallings Dave Bremer Otago Polytechnic,
Inter-Process Communication Mechanisms CSE331 Operating Systems Design.
Tutorial 5 Even More Synchronization! presented by: Antonio Maiorano Paul Di Marco.
© 2004, D. J. Foreman 1 High Level Synchronization and Inter-Process Communication.
Concurrency: Mutual Exclusion and Synchronization Chapter 5.
Lecture 3 Process Concepts. What is a Process? A process is the dynamic execution context of an executing program. Several processes may run concurrently,
Critical Problem Revisit. Critical Sections Mutual exclusion Only one process can be in the critical section at a time Without mutual exclusion, results.
Slide 9-1 Copyright © 2004 Pearson Education, Inc. Operating Systems: A Modern Perspective, Chapter 9.
Slide 9-1 Copyright © 2004 Pearson Education, Inc. Operating Systems: A Modern Perspective, Chapter 9.
Chap 6 Synchronization. Background Concurrent access to shared data may result in data inconsistency Maintaining data consistency requires mechanisms.
Concurrency: Mutual Exclusion and Synchronization Chapter 5.
1 Concurrency: Mutual Exclusion and Synchronization Chapter 5.
1 Concurrency: Mutual Exclusion and Synchronization Chapter 5.
1 Interprocess Communication (IPC) - Outline Problem: Race condition Solution: Mutual exclusion –Disabling interrupts; –Lock variables; –Strict alternation.
UNIX signals & pipes. UNIX Signals A UNIX signal corresponds to an event –It is raised by one process (or hardware) to call another process’s attention.
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.
1 CSCI/CMPE 4334 Operating Systems Review: Exam 2.
CS533 – Spring Jeanie M. Schwenk Experiences and Processes and Monitors with Mesa What is Mesa? “Mesa is a strongly typed, block structured programming.
Operating Systems Lecture Notes Synchronization Matthew Dailey Some material © Silberschatz, Galvin, and Gagne, 2002.
Silberschatz, Galvin and Gagne ©2013 Operating System Concepts – 9 th Edition Chapter 5: Process Synchronization.
© 2004, D. J. Foreman 1 Monitors and Inter-Process Communication.
Deadlock CS Introduction to Operating Systems.
Chapter 5 Concurrency: Mutual Exclusion and Synchronization Operating Systems: Internals and Design Principles, 6/E William Stallings Patricia Roy Manatee.
UNIX signals.
Deadlock and Starvation
Multithreading / Concurrency
CSCI/CMPE 4334 Operating Systems Review: Exam 2
Operating systems Deadlocks.
Semaphore Synchronization tool that provides more sophisticated ways (than Mutex locks) for process to synchronize their activities. Semaphore S – integer.
Chapter 5: Process Synchronization – Part 3
Auburn University COMP 3500 Introduction to Operating Systems Synchronization: Part 4 Classical Synchronization Problems.
Chapter 5: Process Synchronization
Deadlock and Starvation
Chapter 3 – Process Concepts
Chapter 6-7: Process Synchronization
Chapter 9: High-Level Synchronization
Chapter 6: Process Synchronization
CSCI 511 Operating Systems Chapter 5 (Part C) Monitor
Chapter 6: Process Synchronization
Concurrency: Mutual Exclusion and Synchronization
Chapter 5: Process Synchronization (Con’t)
Monitors Chapter 7.
Lecture 25 Syed Mansoor Sarwar
Module 7a: Classic Synchronization
Operating systems Deadlocks.
Critical section problem
Chapter 7: Synchronization Examples
Monitors Chapter 7.
Steve’s Concurrency Slides
Monitors Chapter 7.
Monitors and Inter-Process Communication
Presentation transcript:

High-Level Synchronization

Large Amount of Work to Do

Partition the Work

Define Dependencies 1A 1B 2 3A 3B

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

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

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

First Try Solution Deadlock 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,1,1); fork(philosopher, 1, 0); fork(philosopher, 1, 1); fork(philosopher, 1, 2); fork(philosopher, 1, 3); fork(philosopher, 1, 4); Deadlock

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

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,1,1); fork(philosopher, 1, 0); fork(philosopher, 1, 1); fork(philosopher, 1, 2); fork(philosopher, 1, 3); fork(philosopher, 1, 4);

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

Nesting Semaphore Operations pr P Operation Order ps P Operation Order P(mutex1); P(mutex2); P(mutex2); P(mutex1); <access R1>; <access R1>; <access R2>; <access R2>; V(mutex2); V(mutex1); V(mutex1); V(mutex2); (a) (b) What happens if pr attempts to obtain mutex1 before mutex2, while ps tries to obtain mutex2 before mutex1?  DEADLOCK

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

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 ...

AND Synchronization Given two resources, R1 and R2 Some processes access R1, some R2, some both in the same critical section Need to avoid deadlock due to ordering of P operations Psimultaneous(S1, …, Sn)

Simultaneous Semaphores Def P_sim(semaphore S, int N) { L1: 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 L1; // 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 P_sim); }

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

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

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.sim(int S, int R) { P(mutex); S++; R++; if(((S >= 0) && (R >= 0)) && ((S == 0) || (R == 0))) V(block); V(mutex); }

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,1,1); fork(philosopher, 1, 0); fork(philosopher, 1, 1); fork(philosopher, 1, 2); fork(philosopher, 1, 3); fork(philosopher, 1, 4);

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

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

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.

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 SIGUSR1 & SIGUSR2 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

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#, myHandler) Provides a facility for writing your own event handlers in the style of interrupt handlers Note: default action is to kill the process!

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

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

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

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

Toy Signal Handler (2) static void sig_handler(int signo) { switch(signo) { case SIGUSR1: /* Incoming SIGUSR1 */ printf(“Parent: Received SIGUSER1\n”); break; case SIGUSR2: /* Incoming SIGUSR2 */ printf(“Child: Received SIGUSER2\n”); default: break; } return

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

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

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.

Monitors – cont.

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 sharedBalance { double balance; public: credit(double amount) {balance += amount;}; debit(double amount) {balance -= amount;}; . . . };

Example: Readers & Writers monitor readerWriter_1 { int numberOfReaders = 0; int numberOfWriters = 0; boolean busy = FALSE; public: startRead() { … }; finishRead() { startWrite() { finishWrite() { reader(){ while(TRUE) { . . . startRead(); finishRead(); } fork(reader, 0); fork(reader, 0): fork(writer, 0); writer(){ while(TRUE) { . . . startWriter(); finishWriter(); }

Example: Readers & Writers monitor readerWriter_1 { int numberOfReaders = 0; int numberOfWriters = 0; boolean busy = FALSE; public: startRead() { while(numberOfWriters != 0) ; numberOfReaders++; }; finishRead() { numberOfReaders--; startWrite() { numberOfWriters++; while( busy || (numberOfReaders > 0) ) ; busy = TRUE; }; finishWrite() { numberOfWriters--; busy = FALSE;

Example: Readers & Writers OOPS! Deadlock can happen monitor readerWriter_1 { int numberOfReaders = 0; int numberOfWriters = 0; boolean busy = FALSE; public: startRead() { while(numberOfWriters != 0) ; numberOfReaders++; }; finishRead() { numberOfReaders--; startWrite() { numberOfWriters++; while( busy || (numberOfReaders > 0) ) ; busy = TRUE; }; finishWrite() { numberOfWriters--; busy = FALSE;

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

Condition Variables Essentially an event (as defined previously) Occurs only inside a monitor Operations to manipulate condition variable wait: 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

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

Hoare vs Mesa Semantics Hoare semantics: . . . if(resourceNotAvailable()) resourceCondition.wait(); /* now available … continue … */ Mesa semantics: . . . while(resourceNotAvailable()) resourceCondition.wait(); /* now available … continue … */

2nd Try at Readers & Writers monitor readerWriter_2 { int numberOfReaders = 0; boolean busy = FALSE; condition okToRead, okToWrite; public: startRead() { if(busy || (okToWrite.queue()) okToRead.wait(); numberOfReaders++; okToRead.signal(); }; finishRead() { numberOfReaders--; if(numberOfReaders == 0) okToWrite.signal(); startWrite() { if((numberOfReaders != 0) || busy) okToWrite.wait(); busy = TRUE; }; finishWrite() { busy = FALSE; if(okToRead.queue()) okToRead.signal() else okToWrite.signal()

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

Example: Synchronizing Traffic monitor tunnel { int northbound = 0, southbound = 0; trafficSignal nbSignal = RED, sbSignal = GREEN; condition busy; public: nbArrival() { if(southbound > 0) busy.wait(); northbound++; nbSignal = GREEN; sbSignal = RED; }; sbArrival() { if(northbound > 0) busy.wait(); southbound++; nbSignal = RED; sbSignal = 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 diningPhilosophers { 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: diningPhilosophers() { // Initilization for(int i = 0; i < N; i++) state[i] = THINKING;

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: diningPhilosophers() {...}; pickUpForks(int i) { state[i] = HUNGRY; test(i); if(state[i] != EATING) self[i].wait(); putDownForks(int i) { state[i] = THINKING; test((i-1) mod N); test((i+1) mod N); }

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.

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

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

IPC Mechanisms Message OS IPC Mechanism Must bypass memory protection mechanism for local copies Must be able to use a network for remote copies Info copy Info to be shared Address Space for p0 Address Space for p1

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

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

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

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

Transmit Operations Asynchronous send: Synchronous 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

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

Synchronized IPC Code for p1 Code for p2 /* signal p2 */ syncSend(message1, p2); <waiting …>; /* wait for signal from p2 */ blockReceive(msgBuff, &from); /* wait for signal from p1 */ blockReceive(msgBuff, &from); <process message>; /* signal p1 */ syncSend(message2, p1); syncSend(…) blockReceive(…) blockReceive(…) syncSend(…)

Asynchronous IPC Code for p1 Code for p2 /* signal p2 */ asyncSend(message1, p2); <other processing>; /* wait for signal from p2 */ while(!nbReceive(&msg, &from)); /* test for signal from p1 */ if(nbReceive(&msg, &from)) { <process message>; asyncSend(message2, p1); }else< <other processing>; } nonblockReceive(…) asyncSend(…) nonblockReceive(…) nonblockReceive(…) asyncSend(…) nonblockReceive(…)

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

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

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

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 pipeID[2]; pipe(pipeID); ioctl(pipeID[0], FIONBIO, &on); read(pipeID[0], buffer, len); if(errno != EWOULDBLOCK) { /* no data */ } else { /* have data */

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(eventCount): Announces the occurrence of an event related to eventCount, causing it to be incremented by 1 await(eventCount, v): Causes process to block as long as eventCount < v.

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

More on EventCounts 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