TECHNIQUES FOR DEVELOPING CORRECT, FAST

Transcription

TECHNIQUES FOR DEVELOPING CORRECT, FAST
TECHNIQUES FOR DEVELOPING CORRECT, FAST, AND ROBUST
IMPLEMENTATIONS OF DISTRIBUTED PROTOCOLS
BY
AAMOD ARVIND SANE
THESIS
Submitted in partial fulllment of the requirements
for the degree of Doctor of Philosophy in Computer Science
in the Graduate College of the
University of Illinois at Urbana-Champaign, 1998
Urbana, Illinois
c Copyright by
Aamod Arvind Sane
1998
TECHNIQUES FOR DEVELOPING CORRECT, FAST, AND ROBUST
IMPLEMENTATIONS OF DISTRIBUTED PROTOCOLS
Aamod Arvind Sane, Ph.D.
Department of Computer Science
University of Illinois at Urbana-Champaign, 1998
Roy H. Campbell, Advisor
A distributed system must satisfy three requirements: it should correctly implement process interactions to realize desired behavior, it should exhibit satisfactory performance,
and it should have a robust software architecture that accommodates changing requirements. This thesis presents research that addresses each of these concerns.
The thesis presents new techniques for designing protocols that coordinate process
interactions. The specication technique allows designers to design protocols by topdown renement. Renement steps divide the original protocol into sub-protocols that
have smaller state spaces than the original protocol. Therefore, the divided protocols
can be automatically veried without encountering state-space explosion. The complete
protocol is synthesized by composing the divided protocols.
The thesis also shows how protocols can be tailored for improved performance. A new
technique for designing high-performance distributed shared memory consistency protocols is presented. The technique optimizes consistency protocols by using information
about previous memory accesses to anticipate future communication. Such anticipation
allows communication to overlap with computation, resulting in improved application
performance.
iii
Finally, the thesis presents a software architecture for implementing systems with
interacting distributed objects. The architecture allows systems to be incrementally extended with new objects and new operations, including operations over objects on remote
systems. This is achieved using design patterns, and a novel scheme for incremental construction of state machines. The architecture was used to build a virtual memory system
that is smoothly extended to support distributed shared memory.
iv
TABLE OF CONTENTS
Chapter
1 Introduction : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : :
1.1 Contributions : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : :
1.2 Thesis Outline : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : :
2 A Protocol Design Technique : : : : : : : : : : : : : : : :
2.1 Goal : : : : : : : : : : : : : : : : : : : : : : : : : : :
2.1.1 The Problem : : : : : : : : : : : : : : : : : :
2.1.2 Our Solution : : : : : : : : : : : : : : : : : :
2.1.3 Summary : : : : : : : : : : : : : : : : : : : :
2.2 Background and Related Work : : : : : : : : : : : : :
2.2.1 Verication Systems : : : : : : : : : : : : : :
2.2.2 High-Level Service Specication : : : : : : : :
2.2.3 Synthesis Methods : : : : : : : : : : : : : : :
2.2.4 Our Approach : : : : : : : : : : : : : : : : : :
2.3 The Synthesis Method : : : : : : : : : : : : : : : : :
2.3.1 Synthesis : : : : : : : : : : : : : : : : : : : :
2.3.2 Process and System : : : : : : : : : : : : : : :
2.3.3 Automata : : : : : : : : : : : : : : : : : : : :
2.3.4 Automata and Processes : : : : : : : : : : : :
2.3.5 Protocols : : : : : : : : : : : : : : : : : : : :
2.3.6 Protocol Synthesis : : : : : : : : : : : : : : :
2.4 Specifying Coordination : : : : : : : : : : : : : : : :
2.4.1 Constraint-Rule Specications : : : : : : : : :
2.4.2 Action-Rule Specications : : : : : : : : : : :
2.4.3 Observation-Rule Specications : : : : : : : :
2.4.4 Proving Implementation : : : : : : : : : : : :
2.5 Implementing Constraints, Actions, and Observations
2.5.1 Synthesizing Constraint Rules : : : : : : : : :
2.5.2 Synthesizing Action Rules : : : : : : : : : : :
2.5.3 Observations via Memory and Messages : : :
2.6 Summary : : : : : : : : : : : : : : : : : : : : : : : :
v
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
1
3
4
5
5
5
6
8
9
9
11
12
13
14
15
16
17
18
19
20
22
23
26
33
35
37
38
42
44
45
3 Distributed Shared Memory Consistency Protocols : : : : :
3.1 Goal : : : : : : : : : : : : : : : : : : : : : : : : : : : :
3.1.1 The Problem : : : : : : : : : : : : : : : : : : :
3.1.2 Our Solution : : : : : : : : : : : : : : : : : : :
3.2 Background and Related Work : : : : : : : : : : : : : :
3.2.1 Sequential Consistency : : : : : : : : : : : : : :
3.2.2 Beyond Sequential Consistency : : : : : : : : :
3.2.3 Synchronization in Distributed Shared Memory
3.2.4 Our Approach : : : : : : : : : : : : : : : : : : :
3.3 Coordinated Memory : : : : : : : : : : : : : : : : : : :
3.3.1 Adaptive Barriers : : : : : : : : : : : : : : : : :
3.3.2 Other Adaptive Constructs : : : : : : : : : : :
3.4 Designing Consistency Protocols : : : : : : : : : : : : :
3.4.1 Consistency Specication : : : : : : : : : : : : :
3.4.2 Adaptive Barrier : : : : : : : : : : : : : : : : :
3.4.3 Summary : : : : : : : : : : : : : : : : : : : : :
3.5 Implementation and Performance : : : : : : : : : : : :
3.5.1 Experimental Platform : : : : : : : : : : : : : :
3.5.2 Applications : : : : : : : : : : : : : : : : : : : :
3.6 Summary : : : : : : : : : : : : : : : : : : : : : : : : :
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
47
47
48
50
51
51
54
60
63
64
64
67
68
69
71
72
73
73
74
77
4 A Software Architecture : : : : : : : : : : : : : : : : : : : : : :
4.1 Goal : : : : : : : : : : : : : : : : : : : : : : : : : : : : : :
4.1.1 The Problem : : : : : : : : : : : : : : : : : : : : :
4.1.2 Our Solution : : : : : : : : : : : : : : : : : : : : :
4.2 Background and Related Work : : : : : : : : : : : : : : : :
4.2.1 Basic Objects : : : : : : : : : : : : : : : : : : : : :
4.2.2 Interactions : : : : : : : : : : : : : : : : : : : : : :
4.2.3 Operations : : : : : : : : : : : : : : : : : : : : : : :
4.3 Why the New Architecture : : : : : : : : : : : : : : : : : :
4.3.1 Examples : : : : : : : : : : : : : : : : : : : : : : :
4.3.2 Why Change is not Easy : : : : : : : : : : : : : : :
4.4 What Needs to be Redesigned : : : : : : : : : : : : : : : :
4.4.1 Data Structures and Synchronization : : : : : : : :
4.4.2 Interactions : : : : : : : : : : : : : : : : : : : : : :
4.4.3 A Solution : : : : : : : : : : : : : : : : : : : : : : :
4.5 Architecture of the Virtual Memory System : : : : : : : :
4.5.1 Exporting Functionality : : : : : : : : : : : : : : :
4.5.2 Organizing the Internals : : : : : : : : : : : : : : :
4.5.3 Concurrency Control : : : : : : : : : : : : : : : : :
4.5.4 Operations Using Object-Oriented State Machines :
4.5.5 Implementing Remote Interactions : : : : : : : : :
4.5.6 Dynamic Page Distribution : : : : : : : : : : : : :
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
78
78
78
80
80
80
81
82
82
82
84
86
86
87
88
90
90
93
96
98
104
108
vi
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
4.6 Summary : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : 110
5 Conclusion : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : 112
5.1 Summary : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : 112
5.2 Future Research : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : 114
Bibliography : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : 115
vii
Chapter 1
Introduction
This thesis presents techniques for the design and implementation of protocols that coordinate the actions of concurrent processes in a distributed system. The design of novel
memory consistency protocols for a distributed shared memory system illustrates the
application of these techniques. The system is implemented using a new software architecture for designing object-oriented systems with concurrent and distributed operations.
Protocols are dicult to design because systems of interacting concurrent processes
exhibit a large number of behaviors. Therefore, computer-aided methods are used for protocol design. Currently, such methods can be classied into either verication methods
or synthesis methods. Verication methods let users model the protocols in a suitable
language, and check that model obeys desired properties by exhaustive search of the
system state space. But the detailed, low-level models often result in very large state
spaces. The search is made tractable by exploiting patterns in the state space to reduce the states actually examined. Even so, many practical protocols remain beyond the
reach of exhaustive search. Synthesis methods avoid building complex low-level models.
Instead, they translate high-level specications to low-level implementations. But these
methods often require manual proofs, or are useful only in restricted cases such as peerto-peer communication protocols. Ideally, we would like a design method that combines
the clarity of high-level specications of synthesis methods with the automated checking
characteristic of verication methods.
1
In this thesis, we develop such a design method. We introduce an approach for
dividing the task of protocol design into several steps. The division produces protocols
that have small state spaces either because they are abstract or because they implement
parts of the original protocol. Therefore, their correctness can be easily established
using verication tools. We then show how to implement the divided protocols so that
the complete protocol can be synthesized by combining the divided protocols. We have
applied the synthesis method to guide the implementation of new distributed shared
memory consistency protocols. A distributed shared memory (DSM) system simulates
shared memory over networked computers. DSM systems allow programs designed for
shared memory multiprocessors to be used over networked computers.
DSM systems use local memories of the networked computers as caches for the simulated shared memory. Just like shared memory multiprocessors, caches in DSM systems
replicate the shared data for eciency, but then require protocols to ensure that the
replicas remain consistent.
In this thesis, we develop consistency protocols that allow DSM systems to operate
eciently over wide-area networks characterized by high-latency high-bandwidth interconnections. A protocol that performs well over a wide-area network must be able to
utilize the bandwidth to overcome latency. Our protocols gain their eciency using information about process synchronization and past memory access patterns to predict future
requests from other processes. This technique reduces the time processes spend waiting
for data to arrive. When computations are regular, this anticipatory communication
overlaps communication and computation, giving good speedups for distributed shared
memory programs over wide-area networks.
Protocol implementations derived by our method are state machines that dene protocol behavior. However, the programmer is still left to manage a myriad details of the
implementation environment. In our case, the protocol implementation has to be a part
of a virtual memory system that supports distributed shared memory.
2
In this thesis, we present a software architecture for building object-oriented systems
that have many concurrent operations on groups of objects. The architecture allows the
system to be incrementally extended with new objects and new operations. It smoothly
implements interactions between objects on remote systems. In the course of designing the architecture, we have discovered several design patterns, and a new technique
for constructing state machines incrementally using an object-oriented approach. The
architecture is used to build a virtual memory system. The resulting system is exible: beginning with simple virtual memory facilities, we extended it with facilities like
distributed shared memory in an orderly manner.
1.1 Contributions
This thesis makes the following contributions:
A method for designing process coordination protocols based on
{ A family of notations to express protocols at dierent levels of abstractions.
{ A set of transformations to rene protocols from one level to the next.
{ Application of the method to design memory consistency protocols.
Distributed shared memory consistency protocols that
{ Improve over the performance of existing protocols
{ Perform well over either wide-area and local-area networks.
A software architecture for object systems with concurrent operations on groups of
objects. The architecture is based on:
{ Object-oriented state machines that facilitate construction of state machines
by inheritance, composition and other object-oriented techniques.
{ Design patterns that simplify concurrency control, remote interactions, and
resource management.
3
1.2 Thesis Outline
In Chapter 2 we present our method for synthesizing distributed shared memory protocols. We begin with a review of background and related work and identify our contribution. Next, we chapter present the basic theory and discusses the notations we use at
dierent levels of abstraction. After that we present a set of transformations for synthesizing the protocol implementation from a specication. We then show how interpret the
implementation as a shared memory or message passing program.
In Chapter 3, we develop our consistency protocols. We present the evolution of consistency protocols, and highlight our approach. Then we explain and formally specify our
protocols and comment on the implementation. We conclude this part with performance
results.
In Chapter 4, we present our new software architecture. We use the design of a
virtual memory as the primary example. First we explain the usual architecture of virtual
memory systems, motivating the basic objects and operations. Then we critique it by
considering the impact of changes, and motivate the new architecture. The architecture
is discussed subsequently.
In Chapter 5, we review the contributions and identify problems for future research.
4
Chapter 2
A Protocol Design Technique
In this chapter, we present a new technique for designing nite state process coordination
protocols. We begin by presenting the problem and our solution in brief. Then we
examine the background research in detail, and contrast our solution with it. The rest
of the chapter presents the formal details.
2.1 Goal
2.1.1 The Problem
Protocols that describe the behavior of systems with concurrent interacting components
are dicult to design, because such systems exhibit a large variety of behaviors. A human designer may overlook undesirable interactions in the system, leading to errors such
as deadlock. So an automated method for synthesizing such systems is highly desirable. There are two types of approaches for computer-aided protocol design, verication
methods and synthesis methods. Verication methods help debug previously designed
protocols by exhaustive search of state spaces, while synthesis methods start with protocol specications and translate them to low-level protocol implementations.
We used these methods in our research for designing distributed shared memory
consistency protocols. These protocols describe systems that have a very large number
5
of states. Therefore, we could only verify simplied versions of the protocols. We also
attempted to use synthesis methods. But methods that had tool support are designed
for synthesizing peer-to-peer communication protocols, or OSI protocol stacks. Synthesis
by hand, based on specications with algebraic or logical languages that could describe
multi-party protocols, requires manual proofs of the specication. Such proofs were
practical only for simple versions of the protocol.
Thus, while verication methods are applicable to a wide class of protocols, they
are limited by the need to describe protocols in detail, as well as the limitations of
exhaustive search. On the other hand, synthesis methods provide abstract protocol
description languages, but the methods require manual correctness proofs. Also, the
abstract descriptions can be more dicult to produce than low-level descriptions based
on communicating automata.
This experience suggested the need for a design method that could combine the
desirable attributes of both verication and synthesis methods.
2.1.2 Our Solution
We introduce an approach for dividing the task of protocol design into several steps.
At each step, the protocol is simple enough that exhaustive search is tractable, so that
verication tools can be used to establish correctness. The simplication is achieved
using notations that support abstraction and decomposition.
We introduce a family of notations that are all communicating automata, except
that the communication is expressed at dierent degrees of abstraction. We chose notations based on communicating automata because communicating automata are a familiar
model used in popular verication tools. In the rst step, a designer uses specications
that express process coordination as abstract predicates that suppress details of communication and control. Whole system verication is done at this step. In the second step,
the designer produces implementations of the predicates in a notation that expresses control but not the details of communication. Here we verify each predicate implementation
separately. The design method requires the implementations to have certain properties
6
that permit composition so that the composite system does not have to be veried again.
In the third step, the predicate implementations are translated to protocols that express
details of communication media. Again, these translations obey conditions that allow
safe composition. Composing the translations terminates the protocol synthesis. We
expand on these ideas in the following.
Step 1 In our method, the initial design is specied by automata that describe only the
desired coordination between automata, without saying how it is implemented. Thus, the
initial models are extensional, abstract, and relatively simple. The notation we use formalizes a common way to describe process coordination. For example, mutual exclusion
between two processes is often described as follows: \when one process is in its critical
region, the other should not be in its critical region." Here, we divide the execution of
a process into regions, and express coordination as a predicate on the regions of interacting processes. Our rst notation describes processes as automata, and coordination
as predicates on their states. The notation suppresses details of how processes control
each other to implement the predicates, as well as details of communication, and leads
to models with small state spaces. We use verication tools to verify deadlock freedom,
liveness, and other system properties.
Step 2 The next step in a design is to show how to implement the predicates. Most
distributed systems support communication mechanisms like message passing that allow
one process to control another process unidirectionally. Bidirectional control is achieved
by some form of request-response interaction. Our next model describes how a process
controls another by unidirectional actions. At this level, we do not model elements like
message queues. For example, consider a predicate over two process that says: \when
one process enters region x, the other should enter y". This might be used to describe
the process opening a TCP connection. One implementation might be: \First, the target
waits for the connector request. Then the connector sends its request and waits for the
reply". We formalize such a description with a notation where transitions in one process
7
may enable or disable transitions in another process. We use verication tools to ensure
that such an implementations correctly implements a predicate.
The original, abstract protocol may have several predicates. We establish conditions
to ensure that implementations of the predicates can be composed without loss of correctness. Thus, the abstract protocol can be translated to the lower-level protocol by
translating each predicate separately.
Step 3 In the nal step, we use a notation that models communication media. The
idea is that one process may \observe" the current state of another process and use the
information to choose its transitions. An observation is easily implemented in a message
passing system: a process can request the current state of another process, and wait for
the response. Similarly, in a shared (or distributed) memory system, one process may
observe the state of another process by reading shared (distributed) variables.
Observation protocols are used to implement process control: a transition of process P
that requires process Q to be in a certain state is disabled when Q is in a dierent state.
We use verication tools to ensure that an observation protocol correctly implements
process control, and hence the second-level protocols.
Again, we establish conditions to ensure that implementations can be composed safely.
These conditions carry over from the second-level protocols. Thus, the abstract protocol can be translated to observation-based protocols, and hence to shared memory and
message passing programs.
2.1.3 Summary
Our approach has several advantages. The notations based on communicating automata
are familiar, and allow us to use popular protocol verication tools based on communicating automata. The design approach allows us to simplify protocols, rst by abstraction,
and then by decomposition, so that the state space presented to a verier is smaller. The
abstract predicates on processes can be implemented in various ways, and dierent im-
8
plementations can coexist in a synthesized protocol. Also, we can use known algorithms
to implement the predicates, as long as we ensure that the composition conditions hold.
In this thesis, we develop the formal basis for the approach. We have used it to design
the consistency protocols for distributed shared memory, described in Chapter 3.
2.2 Background and Related Work
We describe some of the previous research on protocol design. We then relate our approach to this work.
2.2.1 Verication Systems
Verication systems (also called model checkers ) such as SPIN [Hol91], SMV [McM92],
and Mur' [Dil96] are designed to verify the correctness of nite state distributed protocols. Each verication system provides a language for a precise and understandable
mathematical model of the system. For instance, SPIN uses communicating nite state
machines [BZ83]. Another formal language allows the user to specify correctness predicates; the systems mentioned above use variants of temporal logic [MP91]. Temporal
logic allows the user to express notions such as \if process P takes action a, Q will
eventually respond with action b". The systems include algorithms that examine the
complete state space of a system and verify that the state graph satises the correctness
criteria [Hol91].
These systems have two drawbacks. First, the modeling languages are at a fairly low
level (messages in SPIN, shared memory in SMV), so that constructing the models is
tedious and error prone. Second, exhaustive exploration of the system state space can
be intractable: this is the state explosion problem. Recent research has concentrated
on developing techniques for checking correctness without exhaustive analysis, as well as
methods for managing large state spaces in limited memory.
Partial-order methods [WG93] attempt to eliminate states that arise from modeling
concurrency as interleaving. If the state transitions of two processes are independent,
9
then in an execution of the system, the transitions may be permuted without aecting
correctness of the execution. Therefore, instead of examining all permutations, we can
examine the state space for an arbitrary permutation of independent transitions and still
check correctness. Moreover, dependencies among the state transitions of a nite-state
system can be approximated by examining the source code. Using such dependency
information, partial order methods guide the search over a limited part of the system
state space.
Symbolic model checking [McM92] uses binary decision diagrams (BDDs) to represent
the state space. The symbolic representation allows compact storage of a large state
space. Algorithms that search the state space to verify correctness can be changed to
operate directly on the BDD representation. BDDs work best for digital circuitry that
has many replicated components. Traditional state exploration may outperform BDDs
for distributed protocols [Hu95].
Fair reachability [GH85, GY84, LM95] methods force state transitions of processes
in a distributed system and explore the resulting state space. This space is smaller than
the state space generated when some processes do not take steps. The smaller space is
sucient to check some properties like deadlocks.
Abstraction [Lon93, PD97] Abstract Interpretation [Lon93], and Composition [Lon93]
based approaches are developed to present model checkers with simpler systems to verify.
These approaches use user-dened equivalence relations [Pon95] induction over replicated
components [McM92], symmetry [Ip96], language containment [Kur94] and similar approaches to eliminate irrelevant states in a system. In methods based on abstraction, it
is enough to check the abstract model to ensure that a property that holds in the abstracted system is really true of the actual system. Methods based on composition and
simulation use theorems that show how to decompose system properties of interest when
verifying components or simulations. All these methods require human intervention.
Methods for managing a large number of states in limited memory include techniques
such as supertrace [Hol91] and hash compaction [WL93]. These methods use hash tables
to remember whether a state has been reached in the exhaustive search. The hash table
10
only stores an approximate description of a state, so that there is a small probability
that one state is mistaken for another. Thus, the exhaustive search omits some states,
and some system errors will not be detected. On the other hand, many more states can
be stored in the same amount of memory, so that approximate search is applicable to
larger systems. State space caching methods [GHP92] use memory as a cache, trading
verication time for memory.
The verication systems have a signicant drawback: the use of low-level models
rst introduces irrelevant system states, and then techniques like partial-order methods
attempt to extract abstract system description.
2.2.2 High-Level Service Specication
Other research such as path expressions [Cam74] and logic of knowledge [HM90] has
concentrated on high-level notations for describing protocols.
Designers often informally describe relationships between the processes in distributed
systems in terms of what one process \knows" about another process. For instance, in
the description of TCP [Pos81] we nd: \An established connection is said to be halfopen if one of the TCPs has closed or aborted the connection at its end without the
knowledge of the other, . . . ". The logic of knowledge formalizes this notion of knowledge
so that programs may include knowledge statements directly without referring to the
method for gaining and losing knowledge. Such knowledge protocols are abstract and
easy to specify [HZ87]. Some results [CM86] hint at ways to implement gain and loss of
knowledge. But so far reducing knowledge specications to actual programs has proven
dicult [FHMV95].
Path expressions [Cam74] are a well-known and easy to use notation for specifying process coordination. Path expressions are regular expressions that describe the
sequences of process activities in a distributed system. Campbell [Cam76] investigates
several variants of path expressions. For some restrictive types of path expressions, it
can be proven a priori that problems like deadlock do not exist, and there are known
11
algorithms to translate such path expressions to low-level P and V operations. But more
expressive notations may be dicult to understand and implement [Hol91].
While verication systems use temporal logic to specify correctness predicates, there
have been attempts to use it to specify systems. Temporal logic has been used as a
programming language [Gab87]. However, descriptions based purely on temporal logic
have proven dicult to understand in practice [Lam94].
2.2.3 Synthesis Methods
Synthesis methods translate a high-level specication to a low level language like communicating nite state machines or CSP.
Tableau based methods [MW84] translate specications in temporal logic to languages
like CSP or Buchi automata. The synthesis method produces a model for the formula as
an existence proof. Tableaus were developed as proof techniques for mathematical logic.
A tableau is a systematic way of decomposing a logical formula into subformulae until we
reach elementary formulae. The truth of elementary formulae can be easily veried, and
the tableau structure ensures that we verify enough elementary formulae to guarantee
the truth of the original formula. When applied to temporal logic, the tableau can be
interpreted as an automaton [MW84]. The automaton is then regarded as a centralized
synchronizer for all processes that interleaves their actions so that the temporal formulae
hold on the resulting sequence of actions. But such a centralized solution is undesirable in
practice. Also, as noted above, descriptions based purely on temporal logic have proven
unwieldy.
Finite State methods are used in synthesizing communication protocols. They begin
with a description of all desirable interactions in the system to be designed, and decompose them into communicating nite state machines. But these methods are often limited
in various ways and appear to be too inexible for use in practice [PS91]. The approach
of specifying desirable interactions seems applicable only to small systems [PS91] and
decomposition is a dicult problem [PS91].
12
In a related method [BZ83], the user starts with a dummy initial state for each process
in the system to be synthesized. The user then species message transmissions for each
process, and the synthesis software deduces the corresponding message receptions. The
software traces all possible states where a reception may occur and updates the receiver
state machine. After each update, the system warns the user if there are states without
any messages in transit and none to be transmitted. Such states correspond to deadlock
situations. Conformance to the service specication is not guaranteed by the method,
although verication methods can be use after the synthesis is complete.
Translation methods [KHvB92] translate specications in notations like LOTOS [BvdLV95]
to message exchanges. The specications dene an ordering of operations, and the translation methods produce state machines that generate the sequences. These are suitable
where service specication can be done as sequences of operations. But specications are
often done in other styles [VSvSB91].
2.2.4 Our Approach
Our work was inspired by research on the logic of knowledge. This research showed
that notions like \a process knows" were enough to express many interesting protocols
succinctly. The treatment by Chandy and Mishra [CM86] reduced the logical operators
to an algebraic form. Path expressions [Cam74] and LOTOS [BvdLV95] were earlier
examples of the use of conjunction and disjunction predicates.
We combined these ideas with the observation that the operators could be regarded as
an abstract form of communication between communicating automata. This combination
leads to succinct specications that can be checked by verication tools developed for
communicating automata.
The next question was how to describe the implementations of constraints without
modeling the peculiarities of communication media. This would allow us to model control
ow without the extraneous system states introduced by communication media. The
model we use here is similar to LOTOS and Path expression operators that permit
13
specifying orders of execution. The novelty is in showing that the implementations can
be composed in way that they do not interfere with one another.
The nal model is similar to the usual communicating nite state machines with
single element queues. The dierence is that we communicate the current state rather
than unstructured values. This makes it easy to translate the protocols to either shared
memory or message passing with optimizations.
Our approach gives a design technique that allows designers to simplify protocols
by decomposition and abstraction. Since our development, we have found that the LOTOSPHERE [BvdLV95] project has informally described the idea of design styles that
mirror our own. They observe that experience shows that early specications are best
described in Constraint-oriented style, while later designs in a State-oriented style.
Our design method can be seen as a formalization of this observation. This unexpected
similarity between our development and LOTOS research has strengthened our belief in
the utility of the method.
2.3 The Synthesis Method
In this section, we present the formal details of the synthesis method. We introduce
our models of process, distributed system, and show how we use automata to denote
processes. Then we discuss our three notations for describing protocols. The rst notation represents communication using abstract operations. The next two notations rene
these operators so that they can be implemented using shared memory and message passing programs. For each notation, we show how to prove that one protocol implements
another. Then we describe some implementations for the abstract communication operators, and show how the implementations can be expressed using shared memory and
message passing.
14
2.3.1 Synthesis
Let Beh be a set of desired behaviors, such as a set of sequences of events. Let Ls be a
specication language and Li an implementation language that specify desired subsets of
Beh . Let the meaning of a specication be given by the function [ :]]s : Ls ! 2Beh , while
the meaning of an implementation by [ :]]i : Li ! 2Beh . Then we dene the problem of
synthesis as follows.
Denition 1 A synthesis method is a total function S : Ls ! Li such that given a
specication , S () denotes the same behaviors as , [[S ()]]i = [[] s.
A classic example of synthesis is the construction of a nite state machine that recognizes
a set of ASCII words !, given a regular expression that species !. Here, Ls is the
language of regular expressions, Li the description of automata, and Beh is the set of all
ASCII words. The synthesis method inductively translates the regular expression into a
nite state machine.
A protocol is a set of rules that describes how processes in a distributed system interact.
For example, a le transfer protocol is a set of rules followed by processes on two machines
in order to transfer les from one machine to another. A protocol synthesis method takes
a description of the externally visible behavior b for a set of processes, and produces
programs that the processes must execute in order to implement behavior b.
We dene processes and distributed systems as sequences of abstract events. The
events represent activities such as memory accesses or message transmission. Protocols are specied using automata. The behavior of each process is specied with an
automaton, and the the joint behavior of a distributed system by the product of these
automata. The state transitions in an automaton that represents a process may depend
on the transitions of automata that represent other processes. The rules that govern this
dependence constitute a model of process communication. Our synthesis method begins
with a high-level specication with an abstract form of communication rules. Through
a series of intermediate steps, the high-level specication is translated to programs that
15
use shared memory or message passing for communication. In the following, we make
these ideas more precise.
2.3.2 Process and System
Denition 2 A process P is a pair (Ep; Rp) where Ep is a nite set of events and Rp is
a set of runs, a set innite sequences over Ep .
The events of two processes are disjoint: for every pair of processes P and Q, Ep\Eq = ;.
Denition 3 A distributed system P is a pair (E ; R) where E is SP 2P EP , and R a set
of system runs, a set of sequences over E such that for every run , 2 R, and every
process P , the projection P is a run of P , P 2 RP .
The runs of processes are specied using automata, and the runs of a distributed system
as the product of the automata of the constituent processes.
16
2.3.3 Automata
Denition 4 A nite state automaton is a tuple of the form (; ; ; ; ), where is
a non-empty nite alphabet, a nonempty nite set of states, a transition relation,
S S , a nonempty set of starting states and a nonempty set
of nal states. Also, all states of are reachable, i.e., for all s 2 , there is a sequence
s0; : : : ; sn where sn = s; s0 2 , and for 0 i < n, (si ; a; si+1) 2 for some a 2 .
Let , be the set of transitions f(s; s0)g such that for some letter a 2 , (s; a; s0) 2 . A
trace t of automaton A on a word w = a0 : : : an,1 in is a sequence of states s0; : : : ; sn
where s0 2 and for every si ; si+1 in t, (si ; ai ; si+1) 2 . Note that a trace can also be
thought of as a sequence of transitions, (s0 ; s1); (s1; s2); : : :. States and transitions that
constitute a trace are said to occur in that trace. The automaton A accepts a word w if
the last state sn is a nal state, sn 2 . The set of all words accepted by an automaton
is the language L of the automaton.
We are interested in automata that accept innite words. Let w = a0 a1 : : : be an
innite word and t be a trace s0; s1; : : : over w. Let the limit of a trace t be the set of
states that appear innitely often in t, lim(t) = fs j s = si innitely ofteng. Then A
accepts w if there is a state s 2 that appears innitely often in t, lim(t)\
6= ;. This
condition, Buchi acceptance, denes Buchi automata. The language of the automaton is
is set of innite words L! that is accepted by the automaton.
Since we specify distributed systems as products of automata, we use a slightly different presentation of the acceptance condition called generalized acceptance [GW94].
Let F = fF1; : : : ; Fk g; Fi 2
; k 0 be a set of sets of accepting states. Then the
automaton A accepts w if for every Fi , lim(t)\Fi 6= ;. If there is only one Fi, then the
condition is the same as Buchi acceptance. Here, A is intended to be the product of
of automata Ai; : : : ; Ak . The condition ensures that accepted sequences are those where
every automaton goes through its accept state innitely often. Thus we enforce fairness
by requiring that every automaton must make progress.
17
Denition 5 A generalized Buchi automaton is a nite state automaton that accepts
innite words under the generalized acceptance condition.
Henceforth, we will assume that every automaton is equipped with a generalized acceptance condition.
2.3.4 Automata and Processes
We use automata to denote processes. Intuitively, we want either automata states or
automata transitions to represent nite sequences of process events. For example, when
describing mutual exclusion, we refer to sequences of critical and non-critical states.
but when describing serial communication, we might use send and receive transitions.
Technically, we achieve this by dening the alphabet to be a set isomorphic to either
the set of states or the set of transitions ,. In the rst case, accepted words dene
acceptable sequences of states. In the second case, accepted words dene acceptable
sequence of transitions.
Correspondence between runs and words is established though a semantic mapping
from letters to process events. Each letter corresponds to a set of nite sequences of
process events. This mapping is inductively extended to map words (sequences over the
alphabet) to runs (sequences over events).
Let A be an automaton and P a process. Let [[]] be a function, [ ]] : A ! 2Ep , such
that for every pair of two distinct letters a; b 2 A , [ a] \[[b]] = ;. The semantics is dened
as follows:
Denition 6 Automaton A = (; ; ; ; ) is said to denote process p = (E; R) if
there exists a (nondeterministic) function [ ]] : A ! 2Ep extended to the words accepted
by A as follows:
[ ] = [ aw]] = [[a]][[w]] for letter a and word w.
18
If is isomorphic to , events of P are represented by the states of A. If is isomorphic
to ,, events of P are represented by transitions of A.
2.3.5 Protocols
Protocols describe the behavior of distributed systems. A protocol is dened by automata
products with restrictions. In a protocol, individual automata denote process behavior,
the product denotes system behavior, and the restrictions on the product model communication. We rst dene the notion of automata products without restriction.
Denition 7 Given A = (A; A; A; A; A ) with acceptance condition FA, and B =
(B ; B ; B ; B ; B ) with acceptance condition FB , and disjoint alphabets and states,
the free product A B = (; ; ; ; ) is dened as follows:
= (A B )[A[B
= A B
= A B
= A B
Given s = (sA; sB ); t = (tA ; tB ); s; t 2 , and a = (aA; aB ); a 2 , (s; a; t) 2 if
(sA ; aA ; tA ) 2 A and (sB ; aB ; tB ) 2 B .
Given s = (sA; sB ); t = (tA ; sB ); s; t 2 , and a = aA; a 2 , (s; a; t) 2 if
(sA ; aA ; tA ) 2 A .
Symmetrically for B.
F = SFi2FA fFi B g[SFi2FB fA Fig
With this denition, A or B may have individual or joint transitions in A B . A state sA
is said to occur in a trace of the product A B if it occurs in some product state (sA; sB ).
Similarly, a transition (sA ; tA ) occurs in a product trace if it occurs either individually
19
or jointly in a product transition. The acceptance condition for innite words requires
that a word be accepted by A B if both A and B pass innitely often through their
accepting states.
In the free product, A and B execute their state transitions independently. But if A
and B communicate, the product cannot have all possible transitions. This observation
motivates the following denition of a protocol.
Denition 8 A protocol p with automata A and B is the free product A B and a
set of restrictions (a subset of all transitions) C that describes an automaton such
that p = , and all the other sets are dened by the states reachable from via the
transitions in , C .
Note that although a protocol is an automaton, we prefer to specify it as a free product
with restrictions. This allows us to reason separately about the structure of component
processes (represented by the free product) and communication (represented by the restriction). Dierent ways of specifying the automata and C are used at dierent stages
of the synthesis method.
2.3.6 Protocol Synthesis
Having dened protocols, we can now dene protocol synthesis. First we dene the
notion of protocols that serve as specication and implementation. This is based on the
standard notion of language substitution [HU79]. A substitution is a mapping from an
alphabet to subsets of 0 . The mapping is used to transform words in a language
L() over to words in a language L(0 ) over 0 . We use substitution to transform a
specication into an implementation.
Let l 2 2 be a a set of nite words over some alphabet . Let (l) be the
letters of used in the words of l. The languages l; m over are distinct if they use
dierent letters, (l)\(m) = ;.
Denition 9 A protocol q implements a protocol p (conversely, p species q)
20
Every automaton of p corresponds to exactly one automaton of q.
There is a nondeterministic renement function : p ! 2q such that for every
pair of distinct letters a; b 2 p , the languages (a) and (b) are distinct, and the
words accepted by q are just the rened words accepted by p (extending to words
by induction).
Let P be an automaton of p and Q be the automaton of q that corresponds to P .
Then there is a nondeterministic function a : P ! 2Q that maps letters of P
to distinct languages, and the words accepted by Q are just the rened accepted
words of P .
This denition captures the ideas that a protocol implementation
is composed from automata with terminating executions,
adds detail to the specication, and
every step of the specication is rened in a distinct way.
Note also that the relation between specication and implementation is dened in terms
of alphabets. Since words can describe either sequences of state or transitions, an implementation may rene either states or transitions.
Finally, notice that the relationship of implementation to specication is dened in
terms of relationships between the components, the automata and their states. Thus, we
reason can about the (innite) words via straightforward induction. We may now dene
protocol synthesis simply as follows:
Denition 10 A protocol synthesis method is a function that given a specication pro-
tocol produces its implementation protocol.
By comparing Denition 6 and Denition 9, it is clear that we can always choose denotations such that implementations that conform to Denition 9 preserve behavior. Indeed,
in practice we rene a protocol several times until the events of the process of interest
21
have a one-to-one relationship to transitions of the protocol; the nal step is a ordinary
shared memory or message passing program.
2.4 Specifying Coordination
Automata in a protocol aect each others' state transitions. The rules that describe the
eects model interprocess communication. We use a variety of rules to specify protocols.
The most extensional, abstract protocol specications are given by Constraint rules.
Constraint-rule specications are implemented by Action -rule specications. Action
rules include more details of communication. In turn, Action-rule specications are
implemented by Observation -rule specications. Observation-rule specications are intensional; they are suciently detailed so that they can be easily translated to shared
memory or message passing programs.
System properties like absence of deadlock and reachability are veried once and for
all at the most abstract level for Constraint-rule specications. The subsequent syntheses
preserve these properties.
A property is dened as a set of words over the alphabet of interest [Alp86]. A
word w has a property if w 2 . A language Li preserves properties of language Ls
if for every property s of Ls, there is a unique property i of Li , and if a word ws has
property s , then the corresponding word wi does not have any property disjoint from
i .
Lemma 1 An implementation preserves properties of its specication.
Proof. From Denition 9, by induction, every word accepted by an implementation
protocol corresponds to exactly one word accepted by the specication protocol. Therefore, a property s of the a specication maps to a a unique property i . Furthermore,
if i is a property disjoint from i , and ws 2 s is a word accepted by the specication
with a corresponding word wi of the implementation, then wi 62 i (and wi 2 i ). Thus,
the implementation preserves properties.
22
Action-rule specications are derived from Constraint-rule specications by translating
constraint rules to action rules, and Observation-rule specications are derived from
Action-rule specications by translating action rules to observation rules. We show that
each translation leads to automata that are implementations of the corresponding specication.
2.4.1 Constraint-Rule Specications
Constraint-rule specications express essential coordination among processes. A specication describes desirable sequences of process behavior as succinctly as possible. Constraintrule specications are extensional: they describe the eects of coordination, but not the
details of how processes implement coordination or properties of communication media.
Constraint-rule specications are designed so that techniques for specifying and verifying
protocol properties are easily applicable. For the purposes of this thesis, two types of
constraints suce. One constraint requires that processes synchronize their behavior,
and the other species that behaviors be disjoint.
In the following, we dene Constraint-rule specications, and show a simple example,
the dining philosophers. We discuss the advantages and disadvantages of this style of
specication. Then we explain how protocol properties can be checked at this level using
verication algorithms.
2.4.1.1 Denitions
Let P = (Ep ; Rp) be a process, and Ep be a partition of the set of events Ep into regions.
This denition formalizes intuitive notions like the \critical region" used to describe parts
of the run of a process. Dene a bijection [[]] : ! Ep. Let A = (; ; ; ; ), where
= and a word corresponds to the sequence of states of the trace on that word. A is
said to be a region automaton if it denotes P using [[]] according to Denition 6. Every
state of a region automaton denotes a distinct region of that process. Constraint-rule
specications are protocols that use region automata.
23
Let A1; : : : ; An be automata of some protocol p. A Constraint is dened to be a a
tuple (si; : : : ; sl ) 2 Ai : : : Al for some set of automata fAi ; Aj ; : : : ; Al g of p. An
automaton Ai appears in a constraint if some state si appears in the constraint. A
constraint c is conjunctive (denoted ( ^ : si ; : : : ; sl )) if for every state (h1 ; : : : ; hn ) of p,
if hj = sj for some automaton Aj , then every hk = sk for all automata that appear
in c. A constraint c is disjunctive (exclusive-or) (denoted ( _ : si ; : : : ; sl )) if for every
state (h1 ; : : : ; hn ) of p, if hj = sj for some automaton Aj , then for all other hk ; hk 6= sk .
Conjunctive constraints force all states of a constraint to appear simultaneously in a
global state, while disjunctive constraints allow exactly one state from a constraint to
appear in any global state.
Denition 11 A Constraint-rule specication is a set of automata A1; : : : ; An with a
set of constraints C that denes a protocol composed of the automata and the smallest
restriction that satises C . Each constrained state in an automaton must be preceded
and followed by internal states that do not appear in any constraint.
The internal states act as placeholders that simplify the renement from Constraintrule specications to Action-rule specications. Protocol 1 shows how to specify a three
process dining-philosophers protocol. We denote automata by giving the states and
transitions. The internal states are not shown in the protocols for simplicity. For example,
before f , there is the state n; after f there is an unseen internal state.
Protocol 1
Automata:
Rules:
P1 : t1 !f1!g1!e1!t1
P2 : t2 !g2 !h2 !e2 !t2
P3 : t3 !h3 !f3 !e3!t3
( _ : f1 ; f3)
( _ : g1 ; g2)
( _ : h2 ; h3 )
24
In the protocol, Pi are the philosophers, ti is the region where a philosopher is thinking,
ei where a philosopher is eating, and fi ; gi ; hi are regions where philosophers pick up one
fork on either side. The constraints require that at any time, only one philosopher may
pick up a fork.
2.4.1.2 Advantages and Disadvantages
Constraint-rule specications are abstract. We specify only the desired eects of communication, abstract from details such as memory variables and queues. In addition, we can
use the generalized acceptance condition of Section 2.3.3 to require that the implementation of disjunctive constraints will be fair. As a result, Constraint-rule specications
result in small models; in Protocol 1, the total state space is at most 43 states. Therefore,
it is easy to see that the protocol is incorrect : the three philosophers may each pick up
forks f , g, and h, and block permanently waiting for the other fork. But this model is
not easily implemented in practice.
By comparison, in systems such as SPIN [Hol91], the problem would be modeled
by processes that communicated over message queues. Both forks and philosophers are
modeled by processes. Suppose that the fork processes had three states, fork-here, forkleft, fork-right. In addition, they remember the last philosopher who had the fork to
implement fairness. This means each philosopher has six states, and the system has
43 63, more than 12000 states for such a tiny system. The advantage of the detailed
model is that if forks indeed represent resources that are accessed by messages, deriving
an implementation is easy.
In our method, we resolve the dilemma between abstraction and implementation by
devising ways to generate implementations from the abstract specications. Therefore,
it becomes feasible to use abstract specications.
2.4.1.3 Verifying Correctness
Two main approaches are used to verify the properties of protocols.
25
Reachability analysis [Wes78] searches the global protocol state space to nd states
or sequences of states that violate correctness properties. For example, a deadlock state
is a system state where no process can take a step.
Model checking [CES86] species the desired properties of a protocol using a (undesirable) property automaton. The transitions of the property automaton are described
by predicates that express properties of the state space of the original system. Thus,
the property automaton describes \bad" runs of the system. The protocol violates the
property if there is any word that is accepted by both the protocol automaton and the
property automaton. Thus, we have to detect cycles in the product of the property
automaton and the protocol automaton.
Both approaches are applicable to Constraint-rule specications specications. For
example, an exhaustive search of the state space of Protocol 1 quickly reveals states
where no process can take a step.
The main problem with these techniques is that exhaustive search can be intractable.
By virtue of their level of abstraction, Constraint-rule specications have small state
spaces for many protocols of interest, so verication is not dicult.
Constraint-rule specications can also exploit the research in minimizing state exploration [WG93]. There has been a great deal of interest in methods for detecting and
exploiting regular patterns in the state space. These methods analyze the models to
determine regularities. When models use variables with assignments, dependency detection can be tricky. In contrast, dependencies among processes are explicitly given by
Constraint-rule specications.
2.4.2 Action-Rule Specications
Action-rule specications implement Constraint-rule specications. They are more intensional, in that they show how processes coordinate their actions to implement constraints.
Rules for coordinating actions are binary, that is, any action in one process may control
at most one action of one other process. This reects usual peer-to-peer communication
26
available in practical systems. For our purposes, we need rules where an action can either
disable or enable other actions, or one out two possible actions may be chosen.
In the following, we dene Action-rule specications and present a simple example.
Then we explain how to translate a Constraint-rule specication into an Action-rule
specication. Constraints in a Constraint-rule specications can be implemented in many
ways in an Action-rule implementation. So an abstract Constraint-rule specication can
specify several Action-rule implementations.
An action is some behavior in one process that aects the behavior of another process.
For example, a process P may set the value of a shared variable read by process Q,
aecting its behavior. Formally, an action is just a transition. We use the word action
to distinguish the transitions of an Action-rule specications from the transitions of
Constraint-rule specications or Observation-rule specications.
Denitions Let P = (Ep; Rp) be a process, and Ep be a partition of the set of events Ep
into regions. Dene a bijection [ ] : ! Ep. Let A = (; ; ; ; ), where = , and a
word corresponds to the sequence of transitions of the trace on that word. A is said to be
a branch automaton if it denotes P using [ ] according to Denition 6. Every transition of
a branch automaton denotes a distinct region of that process. Action-rule specications
are protocols that use branch automata. A transition of a branch automaton is called an
action.
Let A1; : : : ; An be branch automata of some protocol p. An action rule is a pair
of actions ((si ; ti ); (sj ; tj )) from two distinct automata Ai and Aj . Actions refer to the
transitions of constituent automata such as Ai.
Let G be a state of p that appears in some trace. Then the next state H depends on
the actions executed from G. The action that leads from G to H must be enabled in G.
An action is disabled or enabled relative to a product state in a trace. Therefore, an action
disabled (enabled) in one product state of a trace may be enabled (disabled) in another
product state that occurs later in the trace. An Action-rule specications is required to
27
be consistent, so that every action is either enabled or disabled in a product state. The
following rules describe how actions are selected in an Action-rule specications.
An action rule is an enabling rule, ((si ; ti ))(sj ; tj )), if (si ; ti ) is the only action taking
si to ti , and for every product state G = (g1; : : : ; gn ) where gi = ti and gj = sj , there is
a transition in the product to a state H = (h1 ; : : : ; hn ) with hj = tj . At every state in a
trace, all actions that are enabled are executed.
An action rule is a disabling rule, ((si ; ti )6)(sj ; tj )), if (si ; ti ) is the only action taking
si to ti , and for every product state G = (g1 ; : : : ; gn ) where gi = ti and gj = sj , there is no
transition in the product to a state H = (h1 ; : : : ; hn ) with hj = tj . A disabled transition
remains disabled in a trace unless enabled by a subsequent action in that trace.
An action rule is a choice rule, ((si ; ti )_(sj ; tj )), if for every product state G =
(g1 ; : : : ; gn ) where gi = si and gj = sj , there is no transition in the product to state
H = (h1 ; : : : ; hn ) with both hi = ti and hj = tj . Choice is assumed to be fair over a
trace.
An action rule is a condition rule, ((si ; ti ) )
? (sj ; tj )), if (si ; ti ) is the only action taking
si to ti , (sj ; tj ) is disabled by some action (ui ; vi ), and for every product state G =
(g1 ; : : : ; gn ) where gi = ti and gj = sj , there is a transition in the product to state
H = (h1 ; : : : ; hn) with hj = tj . Intuitively, the transition (sj ; tj ) is enabled by (si; ti )
provided (ui ; vi ) has disabled it previously. Otherwise, the transition (sj ; tj ) does not
need enabling.
Denition 12 An Action-rule specication is a set of automata A1; : : : ; An with a set
of action rules R that denes a protocol composed of the automata and the smallest
restriction that satises R. The specication must be consistent so that at every product
state, an action is either disabled or enabled.
Protocol 2 below presents a simple protocol for one-shot mutual exclusion that works
when there are no cycles in the process. Starting with the state (s1 ; s2), either one of the
transitions (s1; t1 ) or (s2; t2 ) is chosen. The selected transition bars the progress of the
other process. That process waits until it is enabled by another transition of the executing
28
process. The states u1; u2 are the critical regions. If (s1; t1 ) is chosen, it disables (t2 ; u2 ).
(t2 ; u2 ) is enabled after P1 executes (u1 ; v1 ).
Protocol 2
Automata:
Rules:
P1 : s1!t1 !u1 !v1
P2 : s2!t2 !u2 !v2
((s1; t1 )_(s2; t2 ))
((s1; t1 )6)(t2 ; u2)), ((s2 ; t2 )6)(t1 ; u1))
((u1 ; v1))(t2 ; u2 )), ((u2 ; v2))(t1 ; u1 ))
In the following section, we show how how to apply Denition 9 to show how Action-rule
specications can implement Constraint-rule specications.
2.4.2.1 Proving Implementation
Denition 9 relates an implementation to a specication via substitution. Recall that
substitution maps the letters of one language to words over 0, transforming the
words of a language L() to those of L(0 ). This transformation over languages can
also dened as a transformation on automata. Let us rst consider the transformation
informally.
Let (s; a; t) be the sole transition of an automaton A, where s is the sole initial state,
t the nal state and a a letter. Let be a renement function that maps the letter a
to some nite language. Then the automaton A can be transformed to an automaton
A0 that recognizes (a) simply by replacing the sole transition by the automaton that
recognizes (a).
Given an automaton with exactly two transitions (s; a; t) and (t; b; u), substitutions
over the letters a and b may be implemented by separately replacing the transitions. If
the replacement automata have exactly one initial and nal nal state, then we may
compose the replacements by identifying the nal state of the rst replacement with the
29
initial state of the second replacement If they have multiple initial and nal states, we can
connect the the nal states of the rst replacement automaton to the initial states of the
second replacement by transitions. The epsilon transitions can be removed by the usual
determinization algorithms. Thus, the translation involves composing the replacement
automaton.
Similarly, an implementation protocol dened using Action-rules is derived from a
specication protocol dened using Constraint-rules by replacing each constraint of a
Constraint-rule specications with Action-rule specications and composing them. We
require that when considered in isolation, each replacement implements a constraint according to Denition 9. The action rules that dene one replacement do not aect the
rules that dene the other replacements. Therefore, one replacement does not interfere with another replacement when composed. As a result, the overall Constraint-rule
specications is implemented by the Action-rule specications produced by composing
the replacements. These ideas are formalized as follows. First we formally describe the
form of replacements that we will use. We use the term replacement when talking about
the implementations of individual constraints in a Constraint-rule specications, while
using implementation to refer to the entire Action-rule specications that results upon
substituting every constraint with its replacement.
Denition 13 An Action-rule replacement is a protocol that implements Constraintrule specications of the form
Automata:
P1 : n1!s1!o1
P2 : n2!s2!o2
Rules:
..
.
Either ( ^ : s1; s2; : : :) or ( _ : s1; s2 ; : : :)
and the states n and o do not appear in any constraint rule; the replacement must have
exactly one initial and one nal state, and must remain an implementation if n and o are
identied.
30
We require that the replacement be an implementation even when n and o are identied
so that cycles in the Constraint-rule specication do not invalidate correctness. Note that
n and o are intended to be internal states that represent what happens before and after
a constraint. Given replacements in this form, we can safely compose the replacements.
The composition is dened as follows.
Denition 14 An Action-rule specication is said to be synthesized from a Constraint-
rule specication given the following construction: Given a set of replacements for every
constraint in a Constraint-rule specications, identify the rst and last states of the
replacement for each transition in the Constraint-rule specications for every automaton.
Replace unconstrained states by a single new transition.
With this denition for composing replacements, it is not dicult to argue that the
composition produces an implementation.
Theorem 1 An Action-rule specication A synthesized from a Constraint-rule speci-
cation C is an implementation of C .
Proof. To prove that A implements C (Denition 9), we have to prove for each au-
tomaton, and for the product, that we can dene a function that maps each letter in the
alphabet of the specication to a distinct nite language of nite words. In this case, the
alphabet is just the possible product states. So we will show that for each product state
the function may be dened.
First, we note that traces of a replacement remain unchanged even when it is used in
the synthesis. Consider a product state in the synthesized Action-rule implementation
such that in this state, some replacement automaton is in its initial state. Then, since
the action rules aect only the actions dened within the replacement, the transitions of
the replacement are unaected by transitions implementing constraints on other states.
Therefore, the replacement transitions can always be executed in every reachable state.
Initial states in a Constraint-rule specication do not take part in any constraint.
They are replaced by a single transition each. Therefore, the initial product has an
image.
31
Each new state is reached by a constraint rule. Each transition therefore executes
transitions of the replacement that implement the constraint. Dene a map such that
the replacement transitions corresponding to the n and o states map to internal states.
The transitions that map to the s states dene a suitable image for the product state.
Thus, we have the result.
If we have a set of replacements, we can produce an implementation. But deriving a
replacement for conjunctive constraints requires use to address the issue of simultaneity.
Simultaneity A Constraint-rule specication allows joint transitions in a conjunctive
constraint: all the processes \simultaneously" satisfy the conjunctive constraint. But
Action-rule specications have no synchronous transitions, however they can dene order
relations like before and after. Therefore, we interpret simultaneity by requiring that
\simultaneous" transitions appear after their predecessors and before their successors.
Formally, we have
Denition 15 Let a function f be the renement function for a branch automaton that
maps the states of a conjunctive constraint over processes Pi such that ( ^ : s1; s2; : : :),
and states ni and oi are the predecessor and successor states. Let ti ; ui ; vi be transitions
in an implementation of the constraint such that f (n1) 7! ni; f (si ) 7! ui ; f (oi ) 7! vi .
Then if in every trace, ui follows all occurrences of ni and precedes all occurrences of vi
for all processes, then for all i, transitions ui are said to be simultaneous.
For example, consider the Constraint-rule protocol
Automata:
Rules:
A1 : n1 ! s1 ! o1
A2 : n2 ! s2 ! o1
( ^ : s1; s2)
One possible replacement is the protocol:
32
Automata:
Rules:
A1 : x1 ! y1 ! z1 ! a1
A2 : x2 ! y2 ! z2 ! a2
((x1 ; y1))(y2 ; z2)), ((x2; y2 ))(y1 ; z1))
((y1; z1 ))(z2; a2 )), ((y2 ; z2 ))(z1 ; a1 ))
We dene the map from implementation to specication so that the transitions (y; z )
map to the s states, while the (x; y) and (z; a) transitions map to n and o states. By
Denition 15, the (y; z ) transitions of the processes are simultaneous.
In the following section, we consider Observation-rule specications and show how
they can replace Action-rule specications.
2.4.3 Observation-Rule Specications
Observation-rule specications implement Action-rule specications. They model process communication at a detailed level. One process can \observe" the state of another
process, and choose one of several state transitions as its next transition. Action coordination is implemented via observations.
In the following, we dene Action-rule specications and present a simple example.
Then we explain how to translate an Action-rule specication into a Observation-rule
specication.
Denitions Observation-rule specications also use branch automata like Action-rule
specications.
Let A1; : : : ; An be (branch) automata of some protocol p.
An observation is a tuple, ((si ; ti ); Xj ) of a transition from one automaton (Ai ) and
a set of states Xj from another automaton (Aj ). A transition (si; ti ) is said to be based
on an observation if there is an observation with that transition. Every transition may
be based on at most one observation.
33
Given two observations ((si ; ti ); Xj ) and ((si ; ui ); Yj ), Xj \Yj = ;. That is, transitions
based on observations are deterministic.
A state sj is observable if there is an observation ((si ; ti ); Oj ) where sj 2 Oj .
The semantics of an observation ((si ; ti ); Xj ) is as follows. Intuitively, automaton Ai
in state si observes Aj in one of the states Xj , \remembers it" and later asynchronously
changes state to ti . The state where Ai remembers the observed state cannot itself be
detected by any other automaton. Formally, we ensure this asynchrony by splitting every
transition into a pair of transitions. Transitions based on an observation implicitly specify
a pair of ordinary transitions, ((si ; t0i ); (t0i ; ti )) with a hidden state t0i . Hidden means that
t0i is not observable.
Observations determine global transitions as follows. Given an observation ((si ; ti ); Xj ),
in every product state H = (h1 ; : : : ; hn ) where hi = si and hj 2 Xj , all successor
states G = (g1 ; : : : ; gn ) have t0i as their ith component, gi = t0i . Further, for every state
F = (f1; : : : ; fn) where fi = t0i , for every state E = (e1 ; : : : ; en) where (F; E ) is a transition, either ei = t0i thtObserv0either
e
states. If P1 sees P2 in n2 and goes to w1 , then P2 observes P1 and goes to w20 , then P1
observed P2 before P2 observed P1. Therefore P1 \wins", and can execute (w1 ; c1). Now
suppose that this protocol is executed cyclically; then the next time around P1 will see
P2 in w20 and go to w10 , \releasing" P2.
Thus this protocol implements fair choice: only one process can execute its (w; c)
transitions at a time, but the next time round the other process will be given a chance.
Protocol 3
Automata:
Rules:
P1 : n1!w1 !c1
: n01!w10 !c01
: n1!w10 !w10
: n01!w1 !w1
P2 : n2!w2 !c2
: n02!w20 !c02
: n2!w20 !w20
: n2!w2 !w2
((n1; w1 ); fn2 ; w2 ; c2g), ((n01; w1 ); fn2 ; w2 ; c2g)
((n01; w10 ); fn02 ; w20 ; c02g), ((n1; w10 ); fn02 ; w20 ; c02g)
((w1 ; w1 ); fn2 ; w2 ; c2g), ((w10 ; w10 ); fn02; w20 ; c02 g)
((n2; w2 ); fn01 ; w10 ; c01g), ((n02; w2 ); fn01 ; w10 ; c01g)
((n02; w20 ); fn1 ; w1 ; c1g), ((n2; w20 ); fn1 ; w1 ; c1g)
((w2 ; w2 ); fn01 ; w10 ; c01g), ((w20 ; w20 ); fn1; w1 ; c1 g)
2.4.4 Proving Implementation
In proving that a Observation-rule specication implements a Action-rule specication,
we use the same denition (Denition 9) and the same argument as in Theorem 1. For
each Action-rule replacement, we produce a Observation-rule replacement. We show
how to compose Observation-rule replacements. Every replacement uses observations
35
limited to its own states, therefore, a replacement is unaected by other replacements
of the Action-rule. Each Observation-rule replacement is assumed to be an implementation of an Action-rule replacement. Thus, we can dene a function as required by
Denition 9, showing that the resulting Observation-rule specication implements the
Action-rule specication, and hence the original Constraint-rule specication.
First dene the form of an Observation-rule replacement.
Denition 17 An Observation-rule replacement is a protocol that implements an Action-
rule replacement. It must have the same number of initial and nal states. Transitions
from initial states and transitions to nal states must not be based on observations, If
an initial (nal) state is substituted by an automaton connected to the replacement only
by the initial (nal) transition, and the automaton is assumed to eventually execute the
initial transition, we may substitute the initial (nal) state in an observation with the
states the automaton without aecting correctness.
Just as Action-rule replacements include n and o states that represent possible successors
and predecessors, initial and nal transitions are intended to represent the context of a
replacement. Since transitions from initial states and to nal states do not involve any
interaction with the other process, all interesting behavior begins after the initial transitions and ends before the nal transition. Therefore, a replacement is not aected by
other replacements; all of their states can be thought of as an undistinguished initial state
or nal state. The condition ensures that only transitions internal to the replacement
aect progress.
When composing one Observation-rule replacement with another, as a notational
shorthand, we let the nal states of a replacement non-deterministically select the initial
state of a successor replacement. A determinization algorithm ensures fair behavior
through the subset construction [HU79].
Denition 18 An Observation-rule specication is said to be synthesized from a Constraint-
rule specication given the following construction: Given a set of replacements for every
constraint in a Constraint-rule specications, let every nal state of a replacement be
36
joined to the initial states of the successor replacement. Replace unconstrained states
by a single new transition. In all observations of a replacement, change initial and nal
states to include all states of all other replacements of the observed process.
Theorem 2 An Observation-rule specication O synthesized from a Constraint-rule
specication C is an implementation of C .
Proof. The proof is similar to that of Theorem 1. We dene the map with the help of
the Action-rule maps.
First, we note that traces of a replacement remain unchanged even when it is used
in the synthesis. Consider a product state in the synthesized Observation-rule implementation such that for some replacement the initial transition is executed. Then, since
the observations aect only the transitions dened within a replacement, execution of
replacements of other constraints have no eect. Therefore, the replacement transitions
can always be executed in every reachable state.
Initial states in a Constraint-rule specication do not take part in any constraint.
They are replaced by a single transition each. Therefore, the initial product has an
image.
Every replacement is an implementation of an Action-rule replacement. So we compose the maps from Observation-rule to Action-rule replacements with the map from
Action-rule to Constraint-rule.
Thus, we have the result.
2.5 Implementing Constraints, Actions, and Observations
In this section, we describe a few ways to implement various rules. We present proofs
that they are implementations. In practice, such proofs are best done with verication
tools.
37
2.5.1 Synthesizing Constraint Rules
We rst show how constraint rules are implemented using action rules.
2.5.1.1 Binary Conjunctive Constraints
A binary conjunctive constraint is of the form:
Protocol 4
Automata:
Rules:
P1 : n1!s1!o1
P2 : n2!s2!o2
( ^ : s1; s2)
We want a replacement in the sense of Denition 13. In the following, we describe two
replacements. In Protocol 5, the conjunctive constraint is interpreted as a two-process
rendezvous. In Protocol 6, it is interpreted as a request-response protocol.
Protocol 5
Automata:
Rules:
P1 : n1!w1 !b1!b01!w10 !n01
P2 : n2!w2 !b2!b02!w20 !n02
((n1; w1 ))(w2 ; b2)), ((n2 ; w2 ))(w1 ; b1))
((b01; w10 ))(w20 ; n02)), ((b02 ; w20 ))(w10 ; n01))
In the construction, the (w; b) transition of each process depends on the (n; w) transition
of the other. Similarly, the (b0 ; w0 ) transitions enable (w0 ; n0 ) transitions. Therefore, the
(b; b0) transitions in every trace are \simultaneous" in the sense of Denition 15. So we
have:
Lemma 2 Protocol 5 is a replacement of Protocol 4.
38
Proof. Dene a map f from the transitions of the replacement to the states of the
specication as follows: f ((b; b0 )) 7! s, f ((n; w)) 7! n, f ((w0 ; n0 )) 7! o.
In every trace, the (b; b0 ) transitions occur between the (n; w) and (w0 ; n0 ), even if n
and o are identied.
Therefore, Protocol 5 is an implementation that meets the criteria of Denition 13.
Another interpretation for a binary conjunctive constraint is request-response communication.
Protocol 6
Automata:
Rules:
P1 : n1!b1!b01!d1!d01!o1
P2 : n2!b2!b02!d2!d02!o2
((b1; b01))(b2; b02))
((d2; d02 ))(d1 ; d01))
Here, P2 waits for P1 to enable (b2; b02). This models the request; the next dependence
between (d2; d02 ) and (d1; d01 ) models the response.
Lemma 3 Protocol 6 is a replacement of Protocol 4.
Proof. Dene a renement map f as follows: f ((n; b)) 7! n, f ((b; b0)) 7! n, f ((d; d0)) 7!
o, f ((d0 ; o)) 7! o, f ((b0 ; d)) 7! s, In every trace, the (b0; d) transitions are simultaneous.
Hence the result.
2.5.1.2 Binary Disjunctive Constraints
A binary disjunctive constraint is of the form:
39
Protocol 7
Automata:
Rules:
P1 : n1!s1!o1
P2 : n2!s2!o2
( _ : s1; s2)
We want a replacement in the sense of Denition 13. In the following, we describe
a replacement. In Protocol 8, the disjunctive constraint is interpreted as symmetric
mutual exclusion. One might also implement it using a token based protocol.
40
Protocol 8
Automata:
Rules:
P1 : n1!b1!w1 !c1!e1!o1
: b1!w10 !c1
P2 : n2!b2!w2 !c2!e2!o2
: b2!w20 !c2
choices rules:
((b1; w1 )_(b2; w2 ))
((b1; w10 )_(b2; w20 ))
enable/disable rules for P1:
((b1; w1 )6)(b2; w20 )), ((b1 ; w1 )6)(w2 ; c2))
((c1 ; e1) )
? (b2 ; w20 )), ((c1 ; e1) )
? (w2 ; c2))
((b1; w10 )6)(b2; w2 )), ((b1 ; w10 )6)(w20 ; c2))
((c1 ; e1) )
? (b2 ; w2 )), ((c1 ; e1) )
? (w20 ; c2))
symmetric enable/disable rules for P2:
((b2; w2 )6)(b1; w10 )), ((b2 ; w2 )6)(w1 ; c1))
((c2 ; e2) )
? (b1 ; w10 )), ((c2 ; e2) )
? (w1 ; c1))
((b2; w20 )6)(b1; w1 )), ((b2 ; w20 )6)(w10 ; c1))
((c2 ; e2) )
? (b1 ; w1 )), ((c2 ; e2) )
? (w10 ; c1))
We show that:
Lemma 4 Protocol 8 is a replacement of Protocol 7.
Proof. Dene the map such that the (n; b) transitions map to n states of Protocol 7,
(e; o) to o states, (w; c) to s states, and the rest to null.
In the construction, the (b; w) transitions disable the (b; w0 ) transitions of the opposite
process, and vice versa. The (b; w) and (b; w0 ) transitions choose between one another.
Thus, consider the state (b1; b2). By the choice rule, only one of (b1 ; w1 ); (b1; w10 ) or
(b2; w2 ); (b2 ; w20 ) may execute. Suppose (b1 ; w1 ) executes. Then it disables (b2; w20 ) and
41
(w2 ; c2). Therefore P1 can safely execute (w1 ; c1). The disabled transitions are enabled
by (c1 ; e1), so that P2 can continue. In all four cases, the situation is symmetric.
Therefore, in all traces, the transitions (w; c) will never occur concurrently. This does
not change if there is a cycle connecting the n and o states.
Therefore, Protocol 8 is a replacement.
2.5.1.3 N-process Constraints
One general way to implement an N-process constraint replacement from 2-process constraint replacement is to use a hierarchical tournament structure. For example, consider
the implementation of a disjunctive constraint between two process, and suppose we
want to implement a three way constraint. Then, we let the \winner" of the two-process
protocol compete with the third process by repeating the two process protocol. Clearly,
the winner of the second round will be the only process that executes the transition
corresponding to the constrained state. Conjunctive constraint implementations can be
composed in a similar way.
2.5.2 Synthesizing Action Rules
Action-rule specications tend to be tedious. We will discuss only one example of an
action rule protocol, the implementation of binary disjunction.
42
Protocol 9
Automata:
Rules:
P1 : n1!t1 !w1!c1!o1
: n01!t01 !w10 !c01!o01
: t1 !w10 !w10
: t01 !w1 !w1
P2 : n2!t2 !w2!c2!o2
: n02!t02 !w20 !c02!o02
: t2 !w20 !w20
: t02 !w2 !w2
observations for P1:
((t1 ; w1 ); fn2 ; w2 ; t2 ; c2; o2 g), ((w1 ; c1 ); fn2; w20 ; o2 g), ((w1 ; w1 ); ft2 ; w2 ; c2g)
((t01 ; w10 ); fn02 ; w20 ; t02 ; c02; o02 g), ((w10 ; c01 ); fn02; w2 ; o02 g), ((w10 ; w10 ); ft02 ; w20 ; c02g)
antisymmetric observations for P2:
((t2 ; w2 ); fn01 ; w10 ; t01 ; c01; o01 g), ((w2 ; c2 ); fn01; w1 ; o01 g), ((w2 ; w2 ); ft01 ; w10 ; c01g)
((t02 ; w20 ); fn1 ; w1 ; t1 ; c1; o1 g), ((w20 ; c02 ); fn1; w10 ; o1 g), ((w20 ; w20 ); ft1 ; w1 ; c1g)
For convenience, Protocol 9 is depicted graphically in Figure 2.1. In the gure, transitions
that show sets of states over the arrow are observation-based; they observe the states of
the other process.
Lemma 5 Protocol 9 implements Protocol 8.
Proof. In Protocol 9 and Figure 2.1, the transitions corresponding to (n; b) and (e; o)
of Protocol 8 have not been depicted to avoid clutter. The other sequences of Protocol 9
transitions map to the transitions of Protocol 8 as follows: f ((n; t); (t; w)) 7! (b; w),
f ((w; c)) 7! (w; c), f ((w0 ; c0 )) 7! (w; c), f ((c; o)) 7! (c; e), f ((c0 ; o0 )) 7! (c; e).
To show that the traces of Protocol 9 constitute an implementation, consider the
following argument. Suppose P1 is in n1, and P2 in n2. Both make progress; suppose P1
43
- t1
n1
twc
0 0 0 0 0
0 0 0 0 0
0 0 0
nw0o
o1 n1
@@ ,,
,n@t w cno t w c o
@
ntwco
,
, R
@ ?
- w?1
w10 t w c - w10
ntwco
w1 t01 ?
?
c01
@
,
,
?
w2 t w c w2
0 0 0
R@ ?
0
n0 wo0
- o1
t02 , ntwco
n0 t0w0 c0 o00 0 0 @
,
@
0
0
n t w c,o ntwco
@
n0 wo0
c1
- t2
n2
o2 ?
?
c02
Process P2
Figure 2.1: Disjunctive Constraint Using Observation Rules
ends up in w1 , then it has seen P2 in either n2 or t2 . Then P1 observes P2 again. If P2
is still in n2, either it will stay in n2, or it will make progress and end up in w20 where it
will wait for P1. This argument applies symmetrically to all possible traces. Thus, when
one process executes a (w; c) transitions, the other cannot.
Hence we have the result.
2.5.3 Observations via Memory and Messages
We briey explain how to convert Observation-rule protocols to shared memory and
message programs. Note that the following translations are easily expressed in formal
models of messages [FLP85] or memory [LAA87].
Messages : States are implemented as local memory bits. A state transition clears
the bit representing the current state, and sets the bit for the next state. Observations may be implemented in two ways over reliable, ordered channels.
{ Pull model : We can use a request-response protocol to implement observations. A process sends a request for the current state of another process, and
the reply is the observation.
44
- w20
nw0o
c2
Process P1
w2 twc
n02
- o2
{ Push model : Every process sends its current state in a message to all processes
that observe that process.
Memory : We code each state as a memory bit in a distributed memory. State
transitions are implemented by setting the bit for the next state, and clearing it for
the current state. Observations are implemented as if statements that decide the
assignment to the next state.
Many optimizations are possible: for example, since observations by one process are often
based on sets of states of a dierent process, we only need to indicate when state changes
in a process can change observations. Therefore, not every state need be explicitly
represented, and we need only enough bits for distinct observations.
2.6 Summary
We have presented a technique for structuring the design of protocols. The rst step
uses automata with abstract coordination operators to produce simple, extensional descriptions of protocols. The descriptions have small state spaces, as they assume fair
implementation of coordination and hide the details of communication and control. As
a result, whole-system properties can be eectively analyzed at this level by verication
tools. In the second step, we rene the coordination operators. The notation at this
level expresses how processes control one another, but hides details of communication
media. We show how to ensure that these renements can be composed safely to rene
the original abstract protocol. In the third step, we translate the rened operator implementations to a notation that reects the behavior of shared memory or message passing
systems. Again, the resulting protocols are small and can be veried by exhaustive search
tools. The translations are themselves safely composable, completing the implementation
of the original protocol.
We have presented the essential aspects of the method. It was developed simultaneously with the target application, distributed shared memory consistency protocols,
45
described in the next chapter. Much remains to be done as regards expressiveness and
tool development before it can be deployed in practice.
46
Chapter 3
Distributed Shared Memory
Consistency Protocols
In this chapter, we present a new set of consistency protocols for distributed shared
memory (DSM). We rst explain the problem we address and present our solution. The
next section discusses related research. We then present our new consistency protocols,
and explain them intuitively. Next we describe how we constructed them using our design
method. We conclude with a presentation of performance results.
3.1 Goal
The goal of this work is to devise consistency protocols for distributed shared memory
that operate eciently over either local area or wide area networks. Communication over
local area networks is characterized by low latency and low bandwidth, whereas wide area
networks have high latency, but high bandwidth. A protocol that performs well over a
wide area interconnect must be able to utilize high bandwidth to overcome high latency.
This is possible by overlapping computation and communication. Therefore, we must
minimize situations where computation waits on communication.
Research in distributed shared memory systems has concentrated on minimizing data
communication. But in our case, reducing data communication alone is not enough; since
47
computation must not wait for communication whenever possible, we also have to reduce
communication needed for synchronization.
In the following, we briey review the basic motivation and design of distributed
shared memory consistency protocols. We then explain our approach.
3.1.1 The Problem
A distributed shared memory system simulates shared memory over networked computers. Distributed shared memory simplies the task of writing parallel programs for several
reasons:
It eliminates the distinction between accesses to local memory and accesses to
remote memory.
It relieves the burden of managing data since data in the memory is persistent and
easy to access.
Since memory can be overwritten, the programmer does not have to explicitly
distinguish between stale and newer data. Old values are simply overwritten and
the latest values are easily accessible.
The shared memory abstraction naturally supports arrays and other familiar data
structures.
The use of shared memory for interprocess communication is well understood from
programming multitasking uniprocessors as well as multiprocessors.
DSM implementations strive to be transparent to the user, so that programs written
for shared memory can be used on distributed shared memory with as few changes as
possible. Shared memory programs have processes that communicate using through
shared variables. When these processes execute over machines with distributed shared
memory, the variable accesses are transparently converted to distributed accesses using
virtual memory hardware. When a variable on a memory page is accessed, if the contents
48
of a shared page exist in the local memory, memory access is allowed. Otherwise, the
access is intercepted, and pages on remote machines are fetched over the network. Thus,
the local memory is treated as a cache of the hypothetical shared memory.
Consistency and Communication Fetching pages over a network takes time. There-
fore, when possible, copies of pages must be kept in local memory. But if a process writes
to its local copy, the copy may become inconsistent with other copies. DSM implementations avoid such data inconsistency by coordinating memory accesses by following a
memory consistency protocol.
A fairly simple protocol for avoiding inconsistencies is to allow only one process to
write to a page at a time. If a process P writes to a page, and another process Q either
reads or writes to that page, the two memory accesses conict. This conict is resolved
by granting write permission rst to one process and then the other. When a process
acquires permission to write, it may acquire a fresh copy of the page and invalidate
the copy held by the previous writer, or update the copy maintained by the previous
writer. In either case, data must be communicated across the network. The challenge in
implementing DSM is to design consistency protocols that minimize communication.
There are many ways to reduce communication. For example, in the single-writer
protocol above, virtual memory hardware forces implementations to detect modications
to pages rather than bytes or words. Thus, writes by processes executing on dierent processors to dierent osets within a page appear to conict. This is called false
sharing. False sharing may be reduced using code rearrangement, choosing small page
sizes, proper data placement, or detecting the actual changes to a page and transmitting the dierence. Other ways to reduce communication include improvements to the
communication infrastructure, using process scheduling to reduce conicts, and allowing
multiple read-only copies of a page. But the communication is dictated primararily by
the single-writer protocol: writes to a page from processes on dierent processors will always require paging across the network. The key to reducing communication is to permit
multiple writers to a page.
49
Weak Consistency Weak consistency protocols allow multiple writers to a page at the
same time. Much of the communication in single-writer protocols happens because writes
to a page are interpreted as writes to a common variable. But in practice, true conicts,
that is, writes to the same variable never occur, because programmers explicitly resolve
such conicts using synchronization constructs such as locks or barriers. Therefore, if
a page is accessed by two processes without rst getting a lock (or a barrier), we can
assume that there is no conict. Copies of a page can then be separately updated. On the
other hand, if a page is accessed from within a synchronization construct, then a conict
is possible. Therefore, writes to a single page must be serialized. Moreover, the page
must be updated prior to synchronization. The performance of DSM implementations
with weak consistency protocols thus depends on how page updates or invalidations are
dictated by the synchronization requirements.
3.1.2 Our Solution
Just as weak consistency models use the presence of synchronization to weaken the consistency requirements, we suggest that it is possible to take into account program memory
access patterns to reduce synchronization requirements. For example, a barrier synchronizes processes so that all processes must gather at the barrier before the barrier opens.
When barriers are used for synchronizing phases of a computation, it ensures that all
processes complete computations in one phase before beginning the next phase. But in
many computations, such strict dependence is not necessary. If we keep track of patterns
of memory access during a computation, we can optimistically begin the next phase and
communicate the results of the previous phase to those processes that are likely to need
them. We detect errors, and back up if necessary. If the computation is relatively regular,
we will succeed more often than we fail, and computation and communication of dierent
processes will overlap.
We use this idea of anticipatory computation and communication to minimize time
wasted waiting for communication. This results in a data-driven model for updating the
results of distributed shared memory. We call this model coordinated memory.
50
In the following, we explain the ideas in detail, and provide a formal specication.
Performance results show that the model achieves the goals: distributed memory computations over wide-area high-bandwidth programs achieve good speedup.
3.2 Background and Related Work
In shared memory multiprocessors, every processor has to access data from the common
shared memory. However, since fetching data from the common memory takes time (i.e.,
has high latency), the data is cached locally with each processor. As a result, the data is
replicated and copies reside in caches that are accessible at high speed (i.e., low latency).
However, whenever a processor modies cached replicated data, somehow all the copies
must be updated, otherwise some copies end up with stale data. Furthermore, if two
processors write to their caches at the same time, the update procedure must choose
between the values written by each processor. These two issues, (1) how to update
replicas and (2) how to choose between simultaneous values, are collectively referred to
as the cache consistency problem. Particular strategies used to address these issues are
called consistency models, consistency protocols, or simply memory models.
3.2.1 Sequential Consistency
A particularly intuitive memory model is called sequential consistency. Under sequential
consistency, the shared memory behavior of a multiprocessor must lead to results that
are similar to those for parallel processes that are interleaved on on a uniprocessor. More
precisely [Lam79],
The result of any execution is the same as if the operations of all the processors
were executed in some sequential order, and the operations of each processor
appear in this sequence in the order specied by its program.
We deduce that for sequential consistency (1) a write must update the cache of every other
processor and all simultaneous reads must occur either before or after that write, and
51
(2) simultaneous writes by dierent processors may be interleaved in any order. These
guarantees are met if we ensure that for any location, only one process may write to it
at any given time. Thus, we can have two types of protocols for keeping any memory
location consistent: either allow only a single-writer process and single-reader process
at a time (SWSR) or allow single-writer but multiple-reader processes (SWMR). Note
sequential consistency imposes no restrictions on simultaneous reads.
3.2.1.1 Sequential Consistency Implementation and Performance
To implement sequential consistency on networked machines (for simplicity, assume that
each machine is a uniprocessor so that every process executes on a separate machine),
we must ensure that only one process may write to a given memory location at a time.
Other processes are prevented from writing to that location at the same time. If a
dierent process writes to that location immediately afterward, that process becomes
the writer, and the permission to access the location is revoked from the rst process.
On the other hand, if a process has the permission to write to a location, and another
process wants to read from that location, we have two alternatives: (1) Either withdraw
the write permission from the rst process and grant it to the second, or, (2) Allow both
processes to only read that location, and revoke all access if any process later writes to
that location, and transfer it to the new writer. Thus, we have to control the read and
right permissions to locations, and transport data and permissions between the machines
as required.
In a seminal paper, Li and Hudak [LH89] showed how to employ virtual memory hardware and message passing over the interconnection network to implement sequentially
consistent distributed shared memory. The virtual memory mechanisms of the operating
system controlling the networked machines are extended to control the read and write
permissions, and to revoke access rights at the granularity of virtual memory pages.
Thus, suppose that a process attempts to write to a memory location within a page
that it cannot access. This attempt results in a page-fault, and the fault handler contacts
52
other machines across the network, locates the page and gets write access as well as the
page data. Similarly, if a process attempts to write to a page that it may on read, there
is a protection-fault, and the fault handler must gain the write permission and revoke
access for all other processes that may have copies.
The implementation must support some directory scheme that allows a process to
locate other writers and readers, and some arbitration scheme to select a writer in case
of multiple requesters. Typically, the directory scheme itself must be distributed for
ecient access and update of directory data.
Now it is easy to see that the sequential consistency model lead to severe performance
problems, and the particular implementation techniques exacerbate them.
Since sequential consistency requires that only one process may write to a memory
location at any time, if two (or more) processes write to a location in succession, the
memory location \bounces" across the network, as the write permission and page data
are exchanged. Thus, a signicant number of write operations require data transmission
across the network and add communication overhead to the computation. Note that this
problem is not an artifact of the implementation, since the single-writer requirement is
imposed by the consistency model, and for every write operation by a distinct process,
obtaining write permission requires network transmission. Thus, sequentially consistent
memory inherently requires signicant network communication; as a result, sequentially
consistent distributed shared memory must be slow and unscalable.
The page-level granularity of the implementation further exacerbates the \bouncing".
The virtual memory system can detect memory accesses to pages, but cannot dierentiate between accesses to dierent osets within a page. Therefore, writes by dierent
processors to dierent osets within a page seem to conict, since they are writes to the
same page. This is called false sharing, because data that is in fact not shared (i.e., data
at dierent osets within a page), appears to be shared.
53
3.2.2 Beyond Sequential Consistency
To improve performance, we can reduce communication by code rearrangement [SMC90],
data placement [SMC90], or by choosing small page sizes [FLR+94]. Similarly, we may
improve the communication infrastructure [SMC90] and develop special network communication protocols. Other techniques include scheduling improvements, so that after acquiring a page, requests from other processes are ignored for some time period
in order to let local computation continue. In our earlier research, we [SMC90] (and
others[FP89, DCM+90, MF90]) investigated such issues.
However, such changes can only lead to minor improvements, since the communication
requirement is dictated by the sequential consistency memory model. Since sequential
consistency requires that only one process may write to a page at a time, writes from
processes executing on dierent processors will always require paging across the network
(\bouncing"). Further scalability problems arise since potentially every computer may
have to be visited to locate a page. Thus, a major improvement is possible only if we can
allow multiple writers, and manage to bound the number of machines that have to be interrogated to locate a page. At the same time, the consistency model must be suciently
intuitive that programmers can easily reason about their programs and implementors of
the model have simple, understandable implementations. In the following, we describe
weak consistency models that allow multiple writers, and also ameliorate problems with
false sharing. Then we argue that the existing models are complex, and that there is
room for further optimizations; this motivates coordinated memory as a simpler model
that also suggests new optimizations.
3.2.2.1 Weakly Consistent Memory Models
Weakly consistent memory models were invented by computer architects [DSB86a, GLL+90].
in order to minimize memory-to-CPU trac by allowing optimizations such as out of
order memory operations, overlapping memory operations, and lock-free caches. For example, if a processor sequentially issues two load instructions i and j to distinct memory
54
locations mi and mj then j may be issued before i completes, thereby overlapping memory reads and reducing overall time. However, in a multiprocessor, this may mean that
some other processor sees the eect of j before i, resulting in incorrect operation. On
the other hand, if no other processor actually reads mi , then the computation would
still be correct, and at the same time the overlapping loads allow the program would
execute faster. But the sequential consistency model forbids such instruction shuing
even though it may improve performance.
To address this problem, weakly consistent models have been dened. The denition
of weakly consistent memories depend on two (interrelated) observations: (1) Since programmers dene their own high-level consistency requirements using locks or barriers,
can we design memory models use this information? (2) Sequential consistency requires
that every process must be able to observe the eects of writes in the same order. Is it
possible to relax this requirement and still reason about correct programs?
The rst question leads to models like release consistency [GLL+90, CBZ95], entry
consistency [BZS93] and other hybrid [AF92] consistency models. The hybrid models
distinguish between synchronization operations and ordinary operations, and the expected order of program memory observations are analyzed as a combination of the two.
Provided programs distinguish between synchronization and ordinary operations, these
models lead to memory behaviors that are indistinguishable from sequential consistency.
The second question leads to models like Pipelined RAM [LS88], and causal consistency [AHN+93, JA94]. These models do not require that all processes be aected by
write and read operations, and allow dierent processes to \see" the eects of operations from dierent processes in dierent orders. Using these models, programmers may
have to rewrite programs that were intended for sequentially consistent memory [AHJ91].
Further, it can be expensive to implement these models, since the implementation must
maintain information about the order of memory accesses for every memory access. Fortunately, it is possible to deduce the order information from programming constructs
such as locks and barriers, thus resulting in hybrid models that are very similar to entry
or release consistency.
55
In the following, we will briey discuss the details of the four models and point out
the drawbacks, thus motivating coordinated memory.
Release Consistency Release consistency [GLL+90, CBZ95] is motivated by the ob-
servation that memory locations that are protected by locks are accessed by only one
process at a time. Further, an accessing process must rst acquire the lock and will be
blocked until the it is released. Thus, the lock holder knows that until it releases the
lock no other process will attempt to access the protected data as any \interleaving" is
forbidden by the lock. Therefore, changes made by a process p to variables protected by
a lock need not become visible to any other process q until after p releases the lock. In
other words, memory should become consistent only upon lock release.
Thus, a release consistent memory implementation can optimize communication in
several ways:
1. It can collect modications made to several variables u; v; w within a critical section
and broadcast them in a single message if reducing messages is important [CBZ95].
2. It may pipeline the writes to u; v; w if hiding write latency is more important than
reducing messages [GLL+90].
3. It may transmit the changed values only to the process that acquires the lock next
[DKCZ93] instead of broadcasting them to all processes.
4. It may piggyback the write values to the acquiring processor on the lock grant
message [DKCZ93].
5. It may delay transmitting the changes, and instead merely inform the acquiring processor about the changed values. The acquiring processor can request the changes
when necessary [DKCZ93].
6. It can make use of access information, such as (1) if accesses will be read-only,
data can be freely replicated, (2) if process modify data one at a time, then the
56
data can be migrated from the lock holder to the next acquiring process. All such
optimization can be brought into play to minimize communication [CBZ95].
7. Since programmers use synchronization to avoid data races (i.e., simultaneous
writes to one memory location), virtual memory based distributed shared memory
systems can assume that pages modied by multiple processes between an acquire
and the corresponding release are in fact modied at dierent osets within a page.
Thus, after the release, the modications can be safely merged. Therefore, pages
that are simultaneously modied do not \bounce" as in sequentially consistent
distributed shared memory.
Selecting the appropriate optimization depends on the tradeos. In true shared memory
multiprocessors [GLL+90], data changes will be pipelined with multiple messages since
hiding write latency is more important than reducing messages. In distributed shared
memory systems [CBZ95], data changes are buered to reduce messages. Other optimizations such as migrating data can be applied in both cases, provided the required
information is available.
Entry Consistency A release consistent memory model updates changes to all vari-
ables upon a release operation. With entry consistency, the programmer creates an
explicit association between a lock and the memory locations it protects, and only the
associated memory locations are made consistent upon the next lock acquire. Thus, entry consistency takes a step beyond release consistency in making eective use of the
information that implicit in a program.
Clearly, entry consistency and release consistency are similar enough that aggressive
optimizations detailed above can be applied to entry consistency to boost performance.
One slight disadvantage of entry consistency is that the programmer must declare the association between the synchronization variables and the data. However, related research
indicates that such modications are not very onerous [JKW95].
57
Causal Consistency Sequential consistency requires that all processes agree on some
global order of memory accesses. Then we say that a read of variable v must return
the value written to v by the \most recent" write. However, as we have seen, enforcing
such a strict order precludes many optimizations that do not aect correctness. So we
seek a weaker consistency model that does not require all write operations to be totally
ordered. Instead, only writes that can aect the behavior of a process should be ordered;
this ordering must allow the writes to be overwritten when they no longer aect process
behavior.
A process p aects another process q when q reads a value written to some variable v
by p. Suppose that as a result of reading v, q writes a value f () to another variable w.
Now if a third process r reads the value f () from w, followed immediately by a read of
v, then r must read the value and not any prior value of v; this restriction denes how
memory locations (variables) are updated. Without some similar restriction, if r were to
able to read any previous value of v, then in the degenerate case the architecture need
not update memory at all and always return the initial values; in that case, computation
would be impossible since processes cannot communicate with one another.
We say that a write operation W is causally related to a read operation R either if
R reads the value written by W , or if R reads from some process o that read the value
written by (but has not itself updated) W . A memory model in which a read operation
may return the value written by one of its causally related writes is said to be causally
consistent [DSB86b, AHN+93].
To see that multiple return values are possible, consider an extension of the example
above such that some process s writes to v simultaneously with p, and r rst reads
w followed by some variable x written by s. The reads of x and w establish causal
relationships between r and s as well as r and p. Furthermore, since p and s wrote
simultaneously to v, the writes to v cannot be ordered. Thus, from the viewpoint of r, v
has two possible causally recent values, and (stated another way, under causal memory
simultaneous writes to a variable spawn multiple copies of that variable). Upon reading
v, r must choose between one of them as the \actual" value. As a consequence, a single
58
causally consistent variable allows multiple writes. Further, since a write must aect
only causally related processes, writes need not be propagated to all processes. Thus,
implementing causal consistency potentially requires less communication than sequential
consistency. Interesting example programs for causal memory are presented in [AHJ91].
Notice that there is a problem with the above description: we have said that writes
must be propagated only to causally related processes; however, until the rst read
establishes causality, a write need not aect any processes at all. This paradox may
be resolved in several ways: (1) Broadcast the values of writes to all processes with
timestamps that allow readers to select causally recent values. But this implementation
requires too much communication. (2) Initialize memory locations to invalidate states
such that readers of these locations must contact possible writers to get initial values.
Later, every read operation (logically) updates all locations with causally recent values
acquired during the read. Both of these implementation have untenable overhead for
determining and transmitting causally recent values.
However, we can decrease the need for communication considerably by realizing that
the programmer implicitly denes high-level causality relationships by specifying synchronization using locks or barriers [DKCZ93, JA94]. For example, it is obvious that
all write operations immediately before a barrier are causally related to read operations
immediately afterward, since a barrier release operation is causally related to all further computation. Thus, memory operations for synchronization alone can be used to
accurately deduce causality, thereby reducing computational overhead for determining
causally recent values and transmitting updates. Such a \hybrid" causal consistency
model turns out to be very similar to release and entry consistency.
3.2.2.2 Communication in Weak Models
In weak memory models, we assume that conicting write operations occur guarded by
synchronization constructs. Therefore, consistency has to be guaranteed at the begin-
59
ning (or the end) of synchronization. Therefore, communication depends on how often
synchronization constructs are accessed.
When a process must synchronize its computation with other processes, it waits for
its pages to be updated, and for the synchronization operations to complete. During this
time, computation is suspended waiting for communication. Since we are interested in
using distributed shared memory over wide area networks, we want to overlap computation and communication as much as possible. Therefore, we study the implementation
of synchronization constructs.
3.2.3 Synchronization in Distributed Shared Memory
Early distributed shared memory systems [SMC90, LH89, DCM+90] attempted transparent emulation of shared memory multiprocessors. Therefore, synchronization was
implemented by supporting test-and-set like operations within the system. As a result
[SMC90], synchronization operations such as spinlocks led to poor performance since
the page bouncing (Section 3.2) caused by competing spinlocks requires communication
across the network.
Weakly consistent systems [CBZ95] moved away from the strict transparency requirement, supporting weak consistency models as well as multiple implementations of release
consistency. The programmer is expected to annotate shared memory programs to indicate the optimizations desired (e.g., whether some data should be migrated, while other
require multiple writers). In a similar vein, the synchronization operations are implemented by message passing libraries that avoid problems like page bouncing.
However, even these implementations require too much communication and signicantly impact application performance. In the following, as an example, we examine the
implementations of locks, barriers, and queues and identify the bottlenecks. Later, we
see how these bottlenecks can be eliminated, and other synchronization constructions
may be suggested.
60
3.2.3.1 Implementing Locks
Locks are used to ensure exclusive access to sets of memory locations. Simple spinlocks
that spin on a location in shared memory exhibit contention. Contention can be reduced
by using queue locks [And90], where processes waiting for the lock enqueue themselves
rather than spin. When the lock holder releases the lock, the next holder in the lock queue
acquires it. In distributed shared memory systems, the queue is distributed [CBZ95] over
all participants. To enqueue itself, a process puts itself on a local queue, and if the lock is
held by a remote machine, the request is forwarded to that machine. If another machine
has grabbed the lock, the request is appropriately forwarded. When the request reaches
a current holder, the request is queued there. When this request reaches the head of the
queue, the requesting node acquires the lock.
In the best case, the lock is held locally. Otherwise, at least one network access is
needed, and the acquisition grant message can return the data. The new holder must
send an acknowledgment to the previous holder to ensure that lock has been denitely
acquired. In the worst case, the acquisition request may have to \visit" all nodes before
it succeeds in entering the queue. When network access is needed, lock requesters must
wait at least twice the network latency time, plus data transfer time. Implicitly, the
sender must also wait for twice the network time.
If such lock accesses could be optimized, in the ideal case the requester should have
the lock and data when needed, and only the sender needs to wait. With coordinated
memory, we show how the programmer can help the system to approximate this case.
3.2.3.2 Implementing Barriers
A barrier synchronizes all processes that participate in the barrier. When the barrier is
active, no process can pass the barrier until all processes (participants) have arrived at the
barrier. Barriers are commonly used in programs to separate phases of a computation.
For example, in methods for the iterative solution of linear equations, successive iterations
are separated by barriers. The barrier ensures that all processes complete their updates
61
before any process can start the new iteration. Thus, every iteration is guaranteed to
use fresh data.
Barriers are typically implemented [CBZ95, BZS93] using a barrier master process
which collects barrier entry requests from barrier participants. In case the request also
contains updates from processes, the master may merge and rebroadcast the merged
results. Since barriers are usually visited by all processes, it would seem that there is
little to be gained by using distributed barriers [HFM88].
However, a barrier entails considerable overhead in both communication and waiting
for all processes to arrive. However, consider that the purpose of a barrier is often to
ensure that only fresh data from earlier phases is used, not that the processes should
synchronize; synchronization is only a means to this end. Thus, if the programmer
has access more specic synchronization constructs that can express this requirement,
the needless synchronization overhead is eliminated. The coordinated memory model
suggests how to enrich the programmer's repertoire of synchronization constructs so that
such information is available to the implementation and may be utilized.
3.2.3.3 Implementing Task Queues
A task queue is used in programs where processes do not have static allocations of work.
Instead, each process starts o by processing some part of the problem data, and enqueues
the result after it is done. Other processes can then pick the data for further processing.
Task queues have been implemented as migratory data [CBZ95] in distributed shared
memory systems. When a process needs to access the queue, the queue is migrated over
to that process, where it can add or remove tasks. Since the information in the queue
must be available to all processes so that they can pick of task as soon as they become
idle, the migratory data transfer is natural. Experiments show that [CBZ95] contention
for the task queue has a signicant impact on application speedup.
Normally, a task queue need not guarantee that the holder of a task has exclusive
access to that data. However, if we observe that in some problems (e.g., parallel quicksort) we can also regard queue access as a request for data and access, then we can
62
distributed the queue implementation without suering contention. With coordinated
memory, we can give a precise specication of the eect of using synchronization constructs on the memory. Thus, it becomes possible to optimize data transfer by achieving
synchronization as a side eect of data transfer (similar to message passing).
3.2.4 Our Approach
Early sequentially consistent distributed shared memory systems [LH89, DCM+90] attempted transparent emulation of shared memory multiprocessors. Arguing that these
systems inherently [LS88] require more communication than programmers need, weakly
consistent models were introduced to reduce communication requirements. These weak
models utilized information about synchronization given by the programmer to identify
and eliminate unnecessary communication.
Modern systems [JKW95, BZS93, DKCZ93] extend this trend to gain eciency by
requiring programmers or compilers to specify more information about the program to the
runtime system. Even so, for many programs these system appear easier to programsince
the programmer has less data management overhead.
Just as it is argued that sequential consistency is overly consistent, we argue that traditional synchronization constructs such as locks, barriers, etc., are overly strong. They
specify more constraints on process interaction than are necessary. Thus, they require
processes to wait for each other more than strictly necessary. By evaluating the relationship between synchronization constructs and memory consistency, and by aggressively
utilizing known patterns of process interaction as a part of synchronization, we show that
performance of distributed shared memory systems can be further improved. The coordinated memory model shows how to specify the relationship between synchronization,
consistency, and known process interaction patterns. In many cases, this requires little
or no changes to existing shared memory programs, and has considerable performance
benets.
63
3.3 Coordinated Memory
In Coordinated Memory, we coordinate memory accesses using information about previous access patterns. We also achieve synchronization as a side eect of data ow.
To motivate the solution, we again consider the barrier example and show how to
optimize it further. Analyzing the optimization leads to the formal denition of the
coordinated memory model.
3.3.1 Adaptive Barriers
When a barrier is used to separate the phases in a iterative computation, changes1 made
to the memory by each process are forwarded to the barrier master. The barrier master
consolidates the changes and sends them to the participants who need the data. But
whatever the optimizations used to transfer data, to go through a barrier, processes have
to synchronize with the barrier master.
Now suppose that the data distribution is known beforehand, and also we know
which processes update which data. As a concrete example, consider the implementation
of nite dierencing using an iterative method involving three processes. Assuming the
data is partitioned as in Figure 3.1: after each iteration, process pairs h1, 2i and h2,
3i must exchange the pages on the boundaries that contain modied data. Normally,
a barrier would be used to insert a boundary to tell the distributed shared memory
implementation that the data should be updated. However, it is possible to ensure that
stale data is not used without explicit barrier synchronization.
Faking a Barrier To the same eect as a barrier, we change the processes so that
after completing an iteration, each process transmits the boundary data areas (indicated
in Figure 3.1) to the process that will use it in the next iteration. For example, consider
processes 1 and 2. When 1 sends the data to 2 and receives an acknowledgment, 1 knows
1
Or simply change lists instead of the actual contents. The changes might be retrieved lazily.
64
Data for
Process 1
Boundary values
needed by 1 & 2
Data for
Process 2
Boundary values
needed by 2 & 3
Data for
Process 3
Figure 3.1: Data Distribution for an Iterative Linear Equation Solver
that 2 has received the new data. Similarly, 2 sends the data to 1 and learns that 1 has
the data. Both 1 and 2 know that neither will proceed until it gets the data, therefore
when both have sent the data and received the acknowledgments, they know that neither
will use stale data. Thus, the explicit barrier is unnecessary; the data transmission also
guarantees synchronization.
However, waiting for an acknowledgment is expensive on a high-latency network.
Thus, further speedup is possible if we can eliminate the wait. Interestingly, it is possible
to eliminate explicit acknowledgments since both pairs of processes must transmit data to
one another. Thus, the data transmission can also be used as acknowledgment. Hence,
in our example, on a reliable network, process 1 can transmit the data to 2 and vice
versa. Both process know that the other will not start the next phase until it receives
the updated data, so once again the barrier is not necessary.
But most networks are not reliable. Reliable transmission is usually achieved using
acknowledgments, so in a \reliable" network, again latency will aect the computation.
It turns out that we can eliminate acknowledgments most of the time even without a
reliable network. The idea is that each process transmits its data to the other, and also
keeps a local version of the data. For example, suppose that process 1 makes a local
copy of the boundary data and transmits it to 2, and begins its computation for the next
65
phase (and symmetrically for 2). If that message does not make it, 2 will explicitly ask
1 for the data; 1 guarantees that until it knows that 2 no longer needs the data it will
keep its local version. Now if the message gets to 2, then 2 can continue its computation
and when it transmits the results before beginning the next phase, 1 will learn that 2
indeed received the data. Thus, by keeping at most two local versions of the data, the
need for the barrier has been eliminated. By assuming that networks are mostly reliable,
this optimistic strategy will be able to avoid explicit synchronization for most iterations.
The transformations described above can be implemented by altering the program
either manually or using compiler techniques. However, we can show that explicit data
manipulation within the program is unnecessary; it is possible to implement an adaptive
barrier that optimizes itself at run time.
Adaptive Barriers In an adaptive barrier implementation, we assume that the data
distribution and the shared boundary data does not change from iteration to iteration.
We begin with a conventional implementation, where processes contact a barrier master
in order to enter a barrier. The barrier master collects the data requests and after
combining them takes further action.
With the static distribution assumption, suppose the barrier is hit after the rst
iteration and every process communicates required boundary data locations to the barrier
master. Now, the barrier master collates the data and informs all processes about the
future requirement of all other processes. The process acknowledge the information, and
then the barrier master allows the processes to proceed. After that, the processes need
never again communicate with the barrier master, since they all know one another's data
requirement. The implicit or data-driven synchronization can be employed for all future
alterations. Thus, we get the ecient barrier without any change to the programs.
For situations where we cannot guarantee that processes share the same memory
locations from iteration to iteration, the adaptive approach can be used, but the tradeos
must be carefully evaluated. For example, if we know that each process will require data
66
receiver must be able to store the data and hand it over to the next acquirer. Thus,
depending on the accuracy of the statically expected communication pattern, we can
avoid communication and make the data available to the receiver when needed. If the
static pattern is violated, the implementation will still be be correct at the cost of extra
data management and message exchange.
Similarly, if we use task queues where the next task request can be anticipated, then
enqueueing process can send the task directly to the desired processor.
This technique allows us to leverage known patterns to enhance performance. With
increasing research in identifying such patterns, we can apply them to distributed shared
memory programs without modifying user programs.
3.4 Designing Consistency Protocols
In this section, we show how to use the design method of Chapter 2 to guide the development of protocols for distributed shared memory. In practice, we developed both the
protocols and the design method concurrently. Experience gained in one area guided the
research in the other area.
The method can be used in two ways. In one, we start top down and rene the
desired specication. We can use the Constraint-rule specications at the high-level for
very simple models, or also model details. Just as we can show that one Action-rule
specication implements another, we can also show that one constraint rule specication
implements another. In the other way, we can model a new implementation at a low
level, for example, using action rules, and attempt to verify that it implements some
desired specication. In the following, we give Constraint-rule example specications for
several distributed shared memory protocols, and suggest possible implementations. We
also show an example of Action-rule specications used to justify the adaptive barrier.
68
3.4.1 Consistency Specication
First consider very high-level specications for ordinary, sequentially consistent distributed shared memory. Protocol 10 species a single writer protocol for two processes.
Protocol 10
Automata:
Rules:
P1 : n1!w1 !n1
P2 : n2!w2 !n2
( _ : w1 ; w2 )
In this protocol, the n states represents the regions where the process has no page, while
the w states when the process has a page and is a writer. The constraint tells us that
the only process may be a writer at a time.
We might interpret this as a mutual exclusion protocol, and implement it using token
rings or any other suitable distributed mutual exclusion algorithm.
Next consider the specication of multiple reader protocol.
Protocol 11
Automata:
Rules:
P1 : n1$w1 $r1$n1
P2 : n2$w2 $r2$n2
( _ : w1 ; w2 )
( ^ : r1 ; r2 )
Here, we have added the state r to represent readers. In this specication, we would
actually like to say that any process can be in either n or r, but at most one process can
be in w. But the specication does not permit multi-state state constraints as yet. So
we get a somewhat clumsy model, but it helps in developing the more general model.
69
In this specication, we can interpret the conjunctive constraint as a group membership protocol, rather than as a barrier. This interpretation would allow us to naturally
model the change from reader to writer as a group leader election.
These two models are very high level. They tell us that the initial specication makes
sense, and that the introduction of reader copies is harmless.
We can also use the abstract specications to consider a few more details.
One way commonly used in distributed shared memory implementations uses a home
site for a page. All data about page copies (group members) is maintained there. This
is a statically determined membership protocol. In another approach, the current writer
serves as the coordinator that grants requests for read copies and write copies. One may
rely on randomizing factors like network delays and the interference of other computations
to ensure fairness. We can model these possibilities into the specication.
Consider a model with the static home page.
Protocol 12
Automata:
Rules:
N : nN !aN !gN !wN
H : nH !aH !fH !gH !nH
: nH !a0H !fH !wH
W : wW !fW !nW
( _ : aN ; aH ; a0H )
( ^ : gN ; gH )
( ^ : fH ; fG )
In this case, we have shown parts of state machines of three processes. N is a process
that requests write access, it is originally in state n with no page. H is the home process
from which N requests the page. W currently has write access. In state a, N asks for
write access, H fetches the page from W (state f ) and grants it to N in state g. The
disjunctive constraints between the ask states a ensures that only one ask succeeds; here
70
the disjunction is interpreted as arbitration by the home site. The fetch and grant (f
and g) constraints are simple request response exchanges. The path for H through a0
illustrates the ow when H itself requests write access.
This specication illuminates the basic exchanges between the automata to implement
the single writer protocol.
Just as we showed that an Action-rule specication can be an implementation of a
Constraint-rule specication, we can also show that one Constraint-rule specication is
an implementation of another. The methods are the same: establish maps that relate
the higher-level specication to the lower-level specication, and show correspondences
between the traces. Here, we can do such an analysis for the entire system, because we
have still managed to abstract communication. It is easy to show that Protocol 12 is an
implementation of Protocol 10.
3.4.2 Adaptive Barrier
So far, we have seen examples of using Constraint-rule specications. For analyzing the
correctness of the adaptive barrier, we use a notation that directly expresses dependencies
by the enabling and disabling transitions. Protocol 13 expresses the dependencies shown
in Figure 3.1.
Protocol 13
Automata:
Rules:
P1 : n1!w1 !b1!b01!w10 !n01
P2 : n2!w2 !b2!b02!w20 !n02
P3 : n3!w3 !b3!b03!w30 !n03
((n1; w1 ))(w2 ; b2)), ((n3 ; w3 ))(w2 ; b2)),
((n2; w2 ))(w1 ; b1)), ((n2 ; w2 ))(w3 ; b3))
((b01; w10 ))(w20 ; n02)), ((b03 ; w30 ))(w20 ; n02))
((b02; w20 ))(w10 ; n01)), ((b02 ; w20 ))(w30 ; n03))
71
In this protocol, process P2 can enter the barrier only when processes P1 and P3 enter
the barrier, while P1 and P3 depend only on process P2. In a regular barrier, all three
processes would depend on one another. Analyzing the traces of protocol shows that the
barrier transitions (b; b0 ) are not simultaneous in the sense that the transitions all occur
after their predecessors and successors. Rather, the reduced dependency allows P1 (or
P3) to proceed to the next iteration of the barrier without waiting for P3 (P1 ) from the
previous transition. But the dependence on P2 ensures that only the second iteration can
be started this way, not the third. Thus, what we have implemented is a barrier in which
computations in two phases can be merged. We satisfy specications where P2 proceeds
when P1 and P3 are done, while the dependency between P1 and P3 is derived implicitly
through their dependence on P2.
More formally, this is expressed by specications like Protocol 14, where P2 synchronizes separately with P1 and P3 through the constraints between the regions g and h. In
a usual barrier, all three processes would synchronize through a conjunctive constraint
on a common region.
Protocol 14
Automata:
Rules:
P1 : h1 !h01
P2 : g2!h2!g20 !h02
P3 : g3!g30
( ^ : h1 ; h2 ), ( ^ : h01; h02 )
( ^ : g1 ; g2), ( ^ : g10 ; g20 )
In this case, the reinterpretation is harmless, so we can accept the implementation.
3.4.3 Summary
In this section, we saw how to model the consistency protocols. But the models also show
that the method has many limitations. We have developed only the basis; the method
72
lacks for constraints involving multiple states in the same process, and ways to specify
constraints for a variable number of processes. Thus, when induction is necessary, we
need a manual check. At present, this is a common failing of specication methods that
use exhaustive search for verication. Future research should reveal solutions.
3.5 Implementation and Performance
This section presents experimental performance results. The experiments are designed
to explore the eects of synchronization related communication, and to see whether
programs execute more quickly with adaptive coordinators.
Coordinated memory was developed as a part of the XUNET/BLANCA project which
explores research issues in wide-area ATM gigabit networks. Therefore, the main problem for implementing distributed shared memory over a wide area network is that of
latency. However, ample bandwidth is available, so we sought a distributed memory
model that would allow bulk-data transfer and optimistic communication to compensate
for the latency bottleneck. Release consistency and entry consistency are two models that
allow bulk-data transfer, and can be extended for optimistic communication. However,
we soon discovered that the synchronization requirements became the bottleneck. The
development of adaptive coordinators is designed to relieve this bottleneck. Thus, the
genesis of coordinated memory is partly a result of the unique experimental platform.
3.5.1 Experimental Platform
Our testbed was a four-node cluster of SGI Onyx workstations, one at the University of
Wisconsin, another at NCSA at Illinois, and two in the Computer Science Department
at Illinois, connected through ATM switches developed at AT&T. We normalized the
latency to 74ms roundtrip on all four nodes using the communication latency between the
NCSA and Wisconsin workstations to calibrate the behavior. The ATM interconnection
was available through locally developed HXA HiPPI to Xunet adaptors that connected
73
TCP2 sockets to the underlying network. The available bandwidth for 4KB buers with
a 1280 KB TCP Window averaged around 103 Mb/s, with a peak of about 140.8 Mb/s.
With 64KB buers, a peak of 189 Mb/s was observed. While the underlying network
is capable of a raw bandwidth of 600 Mb/s, the interconnect with the HiPPI adaptor
and TCP overhead considerably aect performance. For comparison, the experiments
are also executed on a local-area ethernet.
Coordinated memory is implemented as an application program using standard Unix
facilities. The implementation has two major components: the library of adaptive coordinators that are implemented using message passing, and the virtual memory manipulation
system that traps non-coordinator accesses to ensure consistency.
3.5.2 Applications
We selected three applications to evaluate coordinated memory. The rst application,
matrix-multiply, models the trivial case of no coordination. The program multiplies
two 400 400 integer matrices and puts the result in a third matrix. We split the
computation equally between the four processes. Each process independently computes
the result. This application serves as a canonical example of an embarrassingly parallel
application, where coordinated memory allows unrestricted replication of data.
The sequential program runs for 32 seconds. Figure 3.2 shows the speedup for four
cases: with Xunet and Ethernet, with and without pre-replication of the matrices. The
Xunet without replication is slower than the others, but the application is compute
bound, and the dierences are not apparent with only four processors.
The second application, SOR (Successive over relaxation) is an iterative method of
solving partial dierential equations (PDE). The program models the discretized area
over which PDE is solved as a matrix. During each iteration, each matrix element
is updated by averaging the values of its four perpendicular neighbors. The program
2
UDP communication turned out to be very unreliable.
74
3
2+
4
S 3
p
e
e
d
u 2
p
3
2+
Xunet 3
Xunet (adaptive) +
Ethernet 2
Ethernet (adaptive) 3
+
2
+
3
2
1
1
2
Processes
3
4
Figure 3.2: Matrix Multiplication
4
Xunet 3
Xunet (adaptive) +
Ethernet 2
2 Ethernet (adaptive)
+
S 3
p
e
e
d
u 2
p
+2
+2
3
2+
1
1
3
3
2
3
Processes
3
4
Figure 3.3: Successive Over Relaxation
divides the matrix into rows, and each process computes the averages on a row. Only
the boundary elements of each row are shared with other processes. We computed 50
iterations for a 1024 3072 matrix. After each iteration, the processes synchronize on a
barrier. In our case, we experimented with normal and adaptive barriers to explore the
performance impact of adaptive barriers.
The sequential program completes in 119 seconds. Figure 3.3 shows that with the
Ethernet in a local area low-latency network, presence or absence of a barrier does not
have much eect. Some impact is apparent with four processes. However, it can be clearly
seen that for a high-latency network, the dierence between the versions with a normal
75
4
S 3
p
e
e
d
u 2
p
+
3
2
1
1
Xunet 3
Xunet (adaptive) +
Ethernet 2
Ethernet
(adaptive)
2
+
+
+
2
3
2
Processes
2
3
3
3
4
Figure 3.4: Quicksort
barrier versus as adaptive barrier is remarkable. The version with the adaptive barrier
exhibits nearly the same speedup as with a local area network, because the processes do
not wait for one another. They optimistically send the changes to the intended recipient
and continue their computation. This overlaps communication and computation, and
the network latency has little eect; it is also compensated by the bandwidth. With an
adaptive barrier, the speedup is limited only by load imbalance.
The nal application, quicksort, was chosen as an example of an application that
exhibits low speedups with release consistency [CBZ95] due to contention over the queue.
The quicksort uses an explicit distributed queue to coordinate data transfers, which can
even be anticipated in some cases (Section 3.3.1). The program partitions an unsorted
list of 512K integers into sublists. Small sublists are locally sorted with bubblesort, while
larger ones are enqueued into a workqueue. Whenever possible, the enqueueing process
explicitly delegates the task; otherwise, a process that has completed its task must deque
task from the workqueue. Coordinated memory allows this optimization without aecting
user programs, as discussed earlier (Section 3.3.1).
Figure 3.4 shows the speedup for quicksort (for a sequential time of 74 seconds).
With the adaptive queue, the quicksort program exhibits behavior similar to SOR; it
is speedup limited only by load imbalance. With the explicit distributed queue, there
76
is little communication once the tasks are farmed out, and processes rarely wait for
work. However, without the adaptive queue, speedup is severely limited, especially for
the high-latency case.
3.6 Summary
The preceding sections presented a technique for overlapping computation and communication by minimizing the contention and waiting period for synchronization.
The adaptive synchronization structures combine communication with synchronization. In addition, they allow optimistic communication, so that process avoid blocking for
one another. Thus, they boost application performance even over hostile environments
such as wide area networks.
Notice that in our applications, with adaptive coordination, performance becomes
limited by load imbalance. Further, the virtual memory driven communication implies
that data scattered in local memory must of necessity be communicated sequentially. On
the other hand, we have already observed that known patterns of communication can
be used to guess future data requirements. Such patterns could be conceivably used for
guessing load requirements as well as anticipating future communication.
77
Chapter 4
A Software Architecture
In this chapter, we present a new software architecture used to build the Choices virtual
memory and distributed shared memory system. The architecture is useful in any application that involves concurrent operations over groups of objects, including objects on
remote machines. The architecture permits incremental extension that add new objects
and new operations. It uses object-oriented state machines to program the operations,
permitting incremental extension of state-machine driven logic. The architecture also
includes techniques for resource management.
4.1 Goal
We motivate the architecture with virtual memory as the primary example. We show
that current software architectures can lead to change-resistant systems, and suggest
ways to factor the objects so that incremental changes become easy.
4.1.1 The Problem
Let us briey recapitulate the basic concepts of traditional virtual memory systems.
Computer architectures provide hardware that permits memory addresses issued by the
processor to be late-bound to addresses actually used to access physical memory. The
78
hardware maintains a per-process table (or a cache) that maps virtual addresses to physical addresses to facilitate the late-binding. This allows operating systems to implement
per-process virtual address spaces that are far larger than available physical memory.
The physical memory is used to cache the contents of the virtual memory that actually
reside on the secondary disk storage. For various reasons [Tan92], the caching system
manipulates chunks of data called pages rather than the smallest addressable unit of
physical memory. Normally, a virtual memory address transparently maps to a physical
address and memory is accessed. But if a virtual memory access requires a page not
available in physical memory, then a page fault is said to occur. The fault is handled
by virtual memory management software that accesses secondary storage to locate the
desired memory contents and moves them to physical memory. Periodically, relatively unused pages (selected according to a paging policy) from the physical memory are paged
out to disk by a process called the pageout daemon. The pageout daemon must also
remove the virtual to physical address binding when a page is removed from physical
memory. The virtual memory subsystem of an operating system thus interacts with user
level processes, the le system, pageout daemons, the process system, and the hardware.
Modern virtual memory systems further complicate this state of aairs. They support
shared memory between processes, copy-on-write sharing, user-level page management,
distributed shared memory and other facilities. If processes share virtual memory, then
the page-fault system has to contend with simultaneous page faults from multiple processes. The pageout daemon has to manipulate the virtual to physical address maps for
several processes. If a page is shared copy-on-write, then the page-faults system must
copy the page for a write access, but otherwise repair faults as usual. If a page is a part of
distributed shared memory, then page-fault handling may require interaction with other
machines across the network. User-level page management requires the paging system to
make upcalls from kernel level to user level. Thus, page-fault handling becomes complex,
and therefore programming a fault handler is tedious and error-prone. We argue that
with current approaches for virtual memory design result in systems that are hard to
understand and change.
79
4.1.2 Our Solution
In the following, we present a software architecture that simplies the construction of
such modern virtual memory systems. Our architecture factors out common structure
and behavior for virtual memory management systems. The factorization makes it possible to add data structures and logic for new facilities incrementally, starting from a
simple traditional virtual memory system. The design disentangles concurrent interactions between the virtual memory, process, le and networking systems. The resulting
system can be either embedded inside the operating system, or used as an external paging
facility.
4.2 Background and Related Work
We rst explain the architecture used for current virtual memory systems [KN93, RJY+ 87,
Rus91] We begin by analyzing the requirements of virtual memory systems, and suggest
the basic objects for the system. Then we discuss how other parts of an operating system
interact with the virtual memory system. The basic objects together with the interactions
reveal the overall structure of the system.
Next we study how the structure changes when new capabilities like copy-on-write and
distributed shared memory are added to the system. We argue that the usual framework
structure [Rus91, Lim95] results in a virtual memory system that is hard to change. The
arguments motivate an architecture that refactors the framework and reduces spurious
dependencies making it easier to change.
4.2.1 Basic Objects
The basic requirements of the virtual memory system are derived by looking at the primary clients, the user-level processes. A user process issues virtual addresses in its address
space, and the virtual memory system translates them to physical addresses, retrieving
memory contents from disk when necessary. A user process may also manipulate regions
80
(ranges of addresses) of the address space. For example, a process may map les (or
parts thereof) to regions with read, write, or execute permissions. These requirements
suggest the following basic objects:
Domains that represents the address space,
MemoryObjects that represent datasets like les,
MemoryObjectViews that represents parts of memory objects,
MemoryObjectCaches that maintain the physical pages that hold the contents of
MemoryObjects,
and AddressTranslation that manages virtual memory hardware.
The other clients are daemon processes such as networking drivers and pageout daemons. These processes operate on physical pages, mapping or unmapping them to virtual
addresses in dierent address spaces. They may also use physical pages that belong to
the kernel. This suggests the need for a Store object that regulates the distribution of
physical pages between various memory object caches, the kernel, device drivers, and
pages that are unused.
4.2.2 Interactions
The le system, the process system, and the networking also interact with the virtual
memory system. The le system supports operations on MemoryObjects that convey or
retrieve data from the disk. The virtual memory system uses the le system to repair page
faults. The le system uses the virtual memory system to access physical pages used in
le caches. The process system interacts with the virtual memory system to manipulate
AddressTranslation when scheduling and descheduling processes. The networking system
interacts with the virtual memory system to make its pages accessible to the device
driver.
81
4.2.3 Operations
Given the basic objects, let us consider the structure of typical virtual memory operations. The code for the operations is distributed throughout the objects, creating a
framework [JR91]
A page fault is detected at the user-level. Given the process and the virtual address,
the handler invokes a pageFault method on the Domain associated with that that process.
The pageFault determines the memory object, the associated physical page (allocating
one from the Store if necessary), and issues a read request on the memory object.
A pageout request is generated when the Store runs low on available pages. The
pageout daemon visits several MemoryObjectCaches, invoking their pageOut method to
release a few physical pages. The data in the physical pages is written out to the disk using
the write method of the MemoryObject cached by a MemoryObjectCache. The virtual to
physical address maps referring to that page are also altered. These maps, implemented
as AddressTranslation objects are associated with address spaces (Domain). So the pager
daemon must either visit all Domain objects and operate on all AddressTranslations, or
maintain a reverse map.
4.3 Why the New Architecture
The preceding discussion portrays the structure of conventional virtual memory implementations with basic facilities.
We now consider how the virtual memory system changes when new capabilities are
added. We analyze the diculties, identifying parts that must be refactored.
4.3.1 Examples
Consider adding support for shared memory to the above system. Shared memory is
implemented by allowing multiple Domains to map a single MemoryObject, perhaps with
multiple MemoryObjectViews. The addition of shared memory means that multiple virtual
82
addresses from dierent domains may map to the same physical page. The pageout
daemon now requires a physical address to virtual address map that references multiple
Domains or AddressTranslations. Moreover, a pageout operation may conict with multiple
pagein requests, and multiple pagein requests may conict with one another. The policy
that selects physical pages during pageout may also be altered to favor non-shared pages.
This requires alteration to the synchronization code.
Next consider adding support interprocess communication via virtual memory manipulation. For example, to transmit data between process P and Q with dierent address
spaces, the physical page mapped to P 's address space can be mapped to the address
space of Q. The transmission may have dierent semantics: for example, the mapping
for P might be removed after transmission, or the physical page may be mapped with a
copy-on-write permission. Should P or Q write to the data, the physical page is copied,
so that the process that has not modied its copy has the original data. The communication is implemented by determining the Domain associated with the source process and
the physical page associated with the source address. The Domain of the the destination
process is modied to map the destination address, and the AddressTranslation is modied to associate it with the physical page. Like the addition of shared memory, adding
copy-on-write (especially if copies of copies are allowed) requires changes to the maps. It
also requires changes to the synchronization code, since the implementation may simultaneously operate on multiple Domains and AddressTranslations. Unlike the addition of
shared memory, adding copy-on-write requires changes to the pagefault repair code. A
virtual page may now have an extra state, copy-on-write, in addition to the usual write,
read, execute.
Addition of interprocess communication creates additional changes. For example,
Both the le system and the networking system can use virtual memory manipulations
to convey data to and from user level processes to the device drivers. In many cases, this
may be faster than copying data between physical pages.
Finally, let us briey consider adding support for distributed shared memory. Virtual
pages in distributed shared memory have additional states such has-distributed-copies or
83
exists-on-remote machines. Pagefault repair may require the retrieval of page contents
across the network. The system must also implement complex page consistency protocols.
This requires extensive changes to the pagefault routines.
The changes discussed so far may be classied as follows:
Changes to objects that associate information: for example, shared memory requires changes to physical-to-virtual address maps maintained in MemoryObjectCaches.
New objects that add new associations, for example, data structures that remember
pages that are copy-on-write copies.
Changes to synchronization code. The usual implementation strategy is to add
semaphores to the methods of objects. For example, MemoryObjectCache uses
a semaphore to resolve conicts between pagefaults and pageouts. Deadlock is
avoided by ordering the semaphores for various objects in a hierarchy, and ensuring
that dierent virtual memory operations visit various objects in an ascending order [Tan92] When new operations are added, we have to devise suitable semaphore
hierarchies.
Changes to pagefault and pageout procedures, as virtual pages acquire new states,
and handling pagefaults and pageouts becomes more involved.
New interactions between the virtual memory system and the rest of the operating system. These arise when new capabilities of the virtual memory system are
exploited in the rest of the operating system.
4.3.2 Why Change is not Easy
Applying these changes is tedious in the usual framework structure where objects hide
not only implementation details, but also distribute control ow. For example, pagefault
processing consists of a set of method calls beginning with a call by the user-process on
84
a Domain. Domain locates the appropriate MemoryObject and invokes pageFault in turn
locates MemoryObjectCache; the MemoryObjectCache fetches a physical page from Store
and lls the page by invoking MemoryObject::read . In this structure, logically pagefault
processing may simply be understood as the method Domain::pageFault . While this is
logically elegant and attractive, it makes changes more dicult. Adding new objects or
changing existing associations becomes dicult, as assumptions about the organization
permeate the method calls, the selection of method call parameters, and the call chain gets
harder to understand. Using inheritance to redene objects exacerbates the diculty;
the call processing now gets distributed over the inheritance hierarchy in addition to
the objects.Embedding synchronization further complicates matters: the hierarchy of
semaphores used to resolve deadlocks becomes implicit in the structure of the call chain.
Furthermore, many such call chains originating at dierent object appear in the system. For instance, the pageout daemon visits MemoryObjectCaches to remove pages. But
removing pages requires operations on AddressTranslations to alter the map and MemoryObject to move data to disk. This creates a class chain that visits objects in a dierent
order.
Interactions between the virtual memory system and le system multiply the diculties. For example, when a page is removed from a MemoryObjectCache during pageout,
the method MemoryObjectCache::write is invoked. That method invokes the disk driver
to move the data to disk. In turn, the disk driver uses interprocess communication via
virtual memory manipulations that are implemented by the MemoryObjectCache.
Such intermingling of data structures, processing, synchronization and inheritance
makes the virtual memory system very fragile, and changes can be hazardous. We need
a framework structure that separates these aspects, becoming easier to understand and
change.
85
4.4 What Needs to be Redesigned
We have identied the intermingling of various aspects the virtual memory system as the
culprit that makes the software brittle.
In the following, we show how to separate these aspects. Then we discuss how the design can be reected in the program by explicitly representing design features as objects.
The refactoring and reication make it easy to understand the ramications of adding
new virtual memory features.
4.4.1 Data Structures and Synchronization
First consider data structures and synchronization.
In every operation, given some parameter such as the pair of virtual-address-andprocess, the operation decides upon the objects to be visited, and collects related
information such as the physical page associated with the virtual address, the le
in which the page contents are stored, and operates on this information. The
objects in the virtual memory system are primararily tables that implement the
associations. For instance, a Domain maps virtual addresses to MemoryObjects, an
AddressTranslation maps virtual addresses to physical addresses and so on. Changes
to virtual memory either add new maps or change existing maps.
The operations may be invoked concurrently, and many conict with one another.
The conict is apparent only when all of the information necessary for an operation is collected. For example, a pagefault operation may conict with a pageout
operation. That a pagefault conicts with a pageout is known only invocations of
MemoryObjectCache::pageFault and MemoryObjectCache::pageOut identify the same
physical page. Adding new capabilities does not change this nature of conict detection.
86
Conicts between operations are resolved by allowing one operation to proceed
while others wait. An operation that proceeds gains exclusive rights to alter the
data structures. Again, this aspect does not vary when new capabilities are added.
The changes to the data are few and predictable: the classic example is that after
pagefault, a virtual page has an associated physical page, and after pageout, there
is no physical page. The result of an operation depends on the current state. For
example, a pagefault operation may allocated and ll a physical page if necessary,
but if the physical page is present, it need only add a virtual address to physical
address mapping to the AddressTranslation. Detailed analysis shows that the logic of
the operations can be easily programmed as a state machine. When new operations
are added, the state machine must be extended.
4.4.2 Interactions
Next consider interactions with other subsystems:
Consider interactions where the virtual memory systems makes le or networking
requests as in pageout operations. During such a request, the other system make
invoke virtual memory operations on the same physical page that is part of pageout,
leading to an apparent conict. Such conicts due to call cycles must be avoided.
Consider interactions initiated by other systems, such as virtual memory manip-
ulations during interprocess communication. These interactions can be treated as
normal virtual memory operations.
Other interactions are implicit. For example, physical pages are dynamically dis-
tributed in among many entities in the operating system: le system, process system, user allocated memory, device drivers and so on. When new pages are needed
elsewhere, pages allocated to one entity must be deallocated. The most appropriate
a page to be deallocated, and the disposal of its contents depends on the entity, so
we need to distribute the responsibility for deallocation.
87
Interactions across machines in distributed shared memory. Most interactions be-
tween virtual memory and other subsystems are simply implemented by designing
the appropriate interfaces, because the language compiler takes care of the rest.
Distributed shared memory is dierent, because here virtual memory systems interact across dierent machines.
4.4.3 A Solution
We make the aspects discussed above explicit. First consider the call chain and synchronization.
The basic objects of the virtual memory like Domain and MemoryObjectCache implement methods to query and alter table entries.
The call chain for every operation is reied into Operation objects that invokes
the various table methods to implement the operation. For example, pagefault is
implemented with a OpPageFault class.
All the information required to implement an operation is gathered into Parameter
objects. For example, the execution of pagefault begins with the virtual address
and gathers the relevant MemoryObject,MemoryObjectCache, PhysicallyAddressableUnit (an object representing the physical page) and so on. These parameters are
explicitly gathered in ParamPageFault objects.
Every invocation of an operation generates an instance of Parameter objects. These
objects are enqueued and used to detect conicts between operations with explicit
Conict classes.
Since only one of several conicting operation proceeds, the instance of Parameter
for that operation also serves as a token that grants permission to change various
tables as required by the operation.
Our next aspect to made explicit is the logic for the operations.
88
The states of virtual pages are explicitly represented as state objects. Dierent
types of state objects encode dierent states, and the methods of a state object correspond to dierent operations. For example, a virtual page may be in
two states, PhysicalPage and NoPage. The methods PhysicalPage::pageFault and
NoPage::pageFault implement pagefault handling. If there is no page, NoPage::pageFault
will allocate a physical page, change AddressTranslation and update the hardware,
whereas PhysicalPage::pageFault will simply update the hardware.
The interactions are made explicit as follows:
Interactions between the systems are made explicit by dening Interaction classes
whose methods dene the interactions. These are similar in spirit to Operation
classes
The advantages of this structure may be summarized as follows:
Since call chains are explicit in the Operation objects, changes to old operations
are made by dening new classes rather than changing methods of individual basic
objects as in the traditional design.
Explicit Parameter objects help in precisely dening methods that implement conict detection and resolution, rather than implicitly encoding it in semaphore hierarchies. Changes that add new conicts or change the resolution of old conicts
can be explicitly programmed.
The use of parameter classes as permission tokens greatly simplies concurrency
control.
New states and changes to the logic of operations can be explicitly described via
Object-Oriented State Machines as described below.
The preceding discussion gives an overview of the unique aspects of our software architecture. In the following, we describe the architecture in greater detail, highlighting design
decisions as design patterns.
89
4.5 Architecture of the Virtual Memory System
We present the architecture as a series of patterns that are used to solve design problems.
We start from the point of view of users of the virtual memory system. We show
how virtual memory functionality may be exported to users and to other parts of the
operating system. Next we show how to organize the internals of the system by reifying
operations as objects. The design of concurrency control code follows. This completes
the basic aspects of the design.
Three other aspects are taken up afterward. The rst is the implementation of virtual
memory operations using object-oriented state machines. This allows us to smoothly
add complex logic for operations with features like copy-on-write and distributed shared
memory. Then we present architectural features for adding interactions with remote
virtual memory systems, and discuss some design issues for resource management.
4.5.1 Exporting Functionality
The rst design question is how to export virtual memory functionality to user level
processes and other subsystems like the le system. The design can be tricky, because
the virtual memory and other subsystems may use one another's services recursively. We
describe the design in two steps.
First consider exporting virtual memory services without the recursive aspect.
Context : The virtual memory system provides services to many entities like userlevel processes, le system, process system, external pagers and so on. The virtual
memory system services are implemented by dierent objects within the system.
Problem : Although the virtual memory services are implemented by dierent objects within the system, it is vital that other entities do not depend on the internal
structure. If other entities encode knowledge about the internal virtual memory
structure, changing the structure can be dicult. Also, dierent entities may use
90
dierent services provided by the system, and may need to know the internals of
the system to dierent degrees.
Solution : For each interacting entity, dene a Interactor class that describes the
services provided by the virtual memory system. For example, VMInterface is an
Interactor that provides methods like VMInterface::pageFault .
Consequences : The Interactor classes dene entry points to virtual memory system,
and make the dependencies between virtual memory and other systems explicit. It
allows us to change the internal structure of the system without impacting other
subsystems. New services can be provided by extended the Interactors by inheritance. The degree of exposure of the details of the virtual memory system can
manipulated by designing the proper interface. It is also conceptually elegant, in
that the Interactors dene a notion of a single virtual system subsystem.
A drawback of the design is that there are many interfaces. The programmer must
ensure that service denitions are identical in dierent interfaces, and that there
are not unmanageably many variations.
Notes : By itself, this is the Facade [GHJV93] pattern. But as we see below, we
need a variation.
Next, we look at the impact of recursive relationships between virtual memory and other
subsystems.
Context : We have to implement virtual memory services that use services from
other subsystems. In turn, the requested services may recursively use virtual memory services. For example, the virtual memory system may request le system
services during pageout, and in turn the le system requests virtual memory manipulations for the disk driver.
Problem : Although the division of the operating system into a set of interacting
subsystems is convenient, it partitions the code for operating system functions
91
among the entities. Recursive relationships can arise between the facilities provided
by dierent subsystems. This can make it dicult to understand, change and
optimize the overall system. For example, any changes to the virtual memory
system must guarantee that the le system can safely use the virtual memory
system even when the use is reentrant.
Solution : For each interacting subsystem, dene a Interactor class that describes
the services provided by the virtual memory system, and the services requested by
the system. If the two types of services share results or parameters during some
operation, implement the appropriate checks to validate the relationship.
For example, MemoryObjectCache serves as an Interactor between the virtual memory system and the le system. When the virtual memory system invokes pageOut ,
it uses MemoryObject::write provided by the le system. MemoryObject::write recursively invokes MemoryObjectCache::pageFault to map physical pages for disk output.
The recursive call is detected by the MemoryObjectCache, so that it does not interfere with the changes made to MemoryObjectCache as part of pageOut processing
that precedes the MemoryObject::write call.
Consequences : The Interactor reduces coupling between the interacting systems.
Explicitly validating that the virtual memory and le system services may invoke
one another recursively prevents changes to either the le system or the virtual
memory system from violating assumptions. It localizes the assumptions that would
otherwise be implicit in the code.
An Interactor may also serve as a convenient point to cache the results of virtual
memory services.
A drawback is that the explicit validation may be dicult to implement, especially if the interacting entities and interactions proliferate, On the other hand, the
proliferation may be an indication that redesign is necessary.
92
Notes : An Interactor class combines aspects of the
Mediator [GHJV93] and
Facade [GHJV93]. If caching is implemented, it may have elements of Memento [GHJV93].
4.5.2 Organizing the Internals
The next design issue is the internal structure of the virtual memory system. We argued
previously that distributing the behavior for virtual memory operations among the basic
objects creates diculties. The solution is to create Operation and Parameter objects
that use basic objects. The design decisions involved in their design are explained below.
Lastly we show Interactors use Operation and Parameter objects to actually implement
the services.
4.5.2.1 Designing Operations
First consider the design issues for Operation objects.
Context : Virtual memory operations such as pagefault involve interactions between
many objects.
Problem : The behavior of virtual memory operations is distributed among many
objects, so the associations between objects inuence the behavior code. Adding
new facilities to the virtual memory system can change the existing associations,
and add new behavior. Changing the associations can change existing behavior
as a side eect. When adding new behavior, it can be dicult to decide how to
distribute it among the objects. Moreover, it is tedious and error prone to change
many objects in a system for every new operation.
Solution : Collect the behavior in a Operation object that coordinates the basic
objects. The basic objects need only implement object associations, state inquiry
and state alteration functions.
93
For instance, in the original virtual memory design, pagefault processing was distributed among methods of Domain, MemoryObject, MemoryObjectCache and so on.
In our architecture, there is a OpPageFault class with pageFault that manipulates
Domain and other objects during pagefault processing.
Consequences : Explicit Operation objects centralize operation implementations,
making them easier to understand. If associations between objects are changed,
local changes can be made in the Operation objects. New operations are easily
added without changing the basic objects.
On the other hand, the centralized, monolithic behavior can become complex. Then
we need other ways to reduce the complexity.
Notes : In the virtual memory system, Operations indeed become complex. Objectoriented state machines [SC95] were invented to simplify the operations. Thus, we
can successfully use mediators.
4.5.2.2 Data Management
Next, consider the issues for Parameter objects.
Context : Operations such as pagefault have many parameters such as the virtual
address range, physical pages, process, address translation, memory access permissions, memory objects and so on. The parameters have to be communicated to
other subsystems like the lesystem, and are useful in detecting conicts among
operations. Dierent operations require dierent parameters.
Problem : When Operation classes invoke methods of basic objects, dierent meth-
ods need dierent parameters. Similarly, services provided by other subsystems,
procedures that detect conicts among concurrent operations all need dierent
parameters. When the operations change, the parameters change. Therefore, managing the parameters as parameters of method calls is tedious.
94
Solution : Package all interesting parameters into a Parameter object, and dene
update and inquiry methods as well as methods that implement conict detection.
In our design, there is a VMParameter object that gathers all parameters for operations like pagefaults. When we added distributed shared memory, additional
parameters were added by deriving a DSMParameters.
Consequences : Parameter objects reduce the large collection of parameters into a
single object that is easier to manage. It makes the denition of services more
uniform and makes it easier add new parameters for new virtual memory facilities.
It also centralizes operations like conict detection explicit in the code.
Parameter objects can become complex if there are too many parameters. If a
parameter object is used as the sole input to a method, it simplies the interface but
hides details like parameter types and distinctions between readonly and writable
parameters.
Notes : Parameter objects also help in solving synchronization problems and streamlining the interaction of virtual memory and other subsystems.
Finally, we have show how requests from the clients of the virtual memory system use
the Operation and Parameter objects.
Context : Interactors like VMInterface dene methods like VMInterface::pageFault
that are user-level processes. The functions are actually implemented by Operation
classes like OpPageFault that use Parameter objects like VMParameters. Interactors
and Operations carry no state, and may have single instances for the whole system.
Problem : Interactors should be able to invoke dierent types of Operation classes
and create Parameter objects. Hardcoding the details directly in the methods of Interactors means that we would have to replicate the method for all interactors. Also,
it becomes dicult to change details like how to allocate memory for the parameter objects. We also need to create instances of Interactor classes like VMInterface
without resorting to global variables to store the instance.
95
Solution : The construction process for invoking an operation is similar for all methods dened by interactors: locate the corresponding Operation class, instantiate the
appropriate Parameter object with the parameters provided by the interacting entity and pass the Parameter to the Operation.
Dene an abstract Factory class that encodes this procedure as the makeProduct
method, and dene concrete class for each variation.
When classes have single global instance, let Let the class manage the single instance, providing methods like makeInstance , getInstance , and destroyInstance . This
applies to Interactors, Operations and Factorys.
Consequences : Factory classes organize details like memory management involved
in creating objects. But if the details change for some products, it may become
tedious to extend the factory and its subclasses.
Notes : These are the standard patterns Abstract Factory [GHJV93] and Singleton [GHJV93] applied to virtual memory.
The design presented so far shows how to organize the virtual memory interactions and
the implementation of virtual memory operations.
4.5.3 Concurrency Control
Thereafter, our design goal is to clarify the design of concurrency control.
Context : Virtual memory operations query and modify the state of many objects.
When two operations need to modify the state of the same object, we have to
sequentialize the modications. The usual way is to associate semaphores with
the basic objects and ensure that the operations interact with the objects in a
hierarchical fashion so that there are no cycles leading to deadlock.
Problem : Some operations may not visit the objects in the same order. For example, pagein begins by visiting Domains, while pageout begins by visiting MemoryObjectCache. Some operations may recursively visit objects more than once,
96
creating cycles. For example, pageout invokes MemoryObjectCache::pageOut , it
invokes MemoryObject::write , and in turn the disk driver invokes MemoryObjectCache::pageIn (for the kernel). Other operations, like interprocess communication,
may visit multiple Domains and AddressTranslations.
Solution : The use of Operation objects makes the order of object invocation explicit.
Examine all Operation classes and divide them into categories of operations, such
that operations from one category may lead cycles with operations from another
category. Identify operations that may lead to recursive cyclic visits to objects.
Use semaphores to serialize operations across categories. For example, interprocess
communication operations are serialized among one another before they are allowed
to conict with other operations.
Use Parameter objects as tokens to detect recursion. For example, MemoryObject::write passes along a VMParameter object that represents the pageout operation, and also stores it with the MemoryObjectCache that originates the write call.
When the disk driver invokes MemoryObjectCache::pageIn, it passes the same parameter object as a token. The MemoryObjectCache can thus identify the recursion
and avoid deadlock.
The remaining operations operate on objects in a hierarchical fashion. They can
simply use semaphores associated with the objects.
Consequences : The use of Operation and Parameter object makes the concurrency
control explicit. When new features are added, analyzing the eect of the new
features becomes easier.
So far, we have discussed the design of interfaces for interactions between virtual memory and other subsystems, the internal design of virtual memory using Operations and
Parameters, and the design of concurrency control. The remaining aspects of the design
are:
How to use state machines to simplify the design of Operation objects.
97
Interactions between virtual memory systems on dierent machines for implementing distributed shared memory.
Programming the dynamic distribution of pages.
4.5.4 Operations Using Object-Oriented State Machines
In this section, we show how to use program the code for virtual memory operations in our
architecture. We begin with a basic design pattern for programming state machines. We
then present implementation techniques that make it possible to extend state machines
by inheritance. Subclassing and Composition techniques for state machines are described.
These methods are used to extend a basic virtual memory system to add copy-on-write
and distributed virtual memory.
4.5.4.1 Basic State Machines
Let us begin with the state machines.
Context : The behavior of virtual memory operations can be dened as a change in
the state of the virtual memory data structures. For example, pagefault changes
the state of a virtual page from Mapped (physical page is mapped to virtual page)
to Unmapped (no mapped physical page) while pageout changes it from Unmapped
to Mapped.
Problem : Usually, the state of an object is maintained as values of its instance
variables. If the behavior during a method call depends on the current state, then
the method is programmed using if or case statements. The state is implicit in
the variables, and the transitions are implicit in the variable assignment. Such a
monolithic organization is dicult to understand. If a new state is added, several
cases and methods must be updated together, complicating code maintenance.
98
Alternatively, and explicit state table can be used. The uniform format makes transitions explicit, but the logic for selecting transitions and actions is still implicitly
programmed as tests and assignments on state variables.
Solution : Represent the state directly using state objects, one object for each state.
The behavior of the actual object is implemented as methods of the state objects.
The object maintains a pointer to the current state object and delegates methods
to that object. Methods of the state objects return the next state.
For example, in the original design, MemoryObjectCache maintains tables of pages
and their current state, and implement pageFault and pageOut using conditional
statements. In our design, these methods are delegated to Mapped and Unmapped
objects, and only pointers to these objects are maintained in the MemoryObjectCache.
Consequences : The states and transitions are explicit, and the appropriate transi-
tion is selected by examining a single variable. The changes to variables that dene
the state are grouped within the methods of state objects. The organization simplies the task of adding state or making other changes. In addition, such changes
do not aect the delegatee. For instance, adding new states VMPageReadOnly and
VMPageWritable instead of VMHasPage will change the methods of VMNoPage, but
not aect MemoryObjectCache.
Representing state using objects (the State [GHJV93] pattern) simplies Operation
classes. When new states are added, or existing states change, we implement the changes
by creating new state classes and suitably altering the methods for existing state objects.
For example, consider the state machine in Figure 4.1 for simple virtual memory, and
the state machine in Figure 4.2 that implements copy on write. In Figure 4.1, a virtual
memory page may be mapped into physical memory so that it is accessible. The page
may be unmapped to store it on backing store and release physical memory for other use1.
1
For simplicity, the state machine diagrams do not show loops (transitions that do not change state).
99
pageOut
Mapped
Unmapped
WMapped
makeCopy
pageAccess
Figure 4.1: Page States
in a Simple Virtual
Memory System
RMapped
pageOut
pageRead/pageWrite
pageWrite
pageOut
pageRead
WUnmapped
makeCopy
RUnmapped
Figure 4.2: Page States for Copy-On-Write
The state machine in Figure 4.2 supports copy-on-write. COW allows data created by
one process to be shared with a dierent process without requiring the data to be copied.
Instead, the physical pages on which the data resides are shared between processes until
the processes modify them. Data is \copied" by mapping the associated physical page
into the virtual address space of the target process with read-only access. However,
upon a write access, the \copied" data is duplicated by copying the page to a new
physical page and changing the read-only access to write access. The two gures show
several similarities, for example states RMapped, WMapped, are similar to Mapped and
methods pageRead , pageWrite are similar to pageFault . The two gures have dierences
corresponding to the additional behavior, for example, makeCopy is added and pageWrite
causes transitions from RMapped to WMapped.
4.5.4.2 Derived State Machines
One way to implement the copy-on-write state machine is to copy the code of the original
state machine and alter it as necessary. However this makes code maintenance dicult.
A better alternative is to express the relationship directly, by considering the copy-onwrite machine to be a subclass of the original machine. For example, can we derive both
100
instances of pageOut in Figure 4.2 be dened by inheriting the pageOut method? If so,
we could program the copy-on-write machine as follows:
Derive pageRead and pageWrite from pageFault , and
Program new methods like makeCopy .
One solution is as follows:
Context : We have implemented the virtual memory state machines using the state
pattern. We want to derive both WMapped::pageOut , RMapped::pageOut by inheriting from the method Mapped::pageOut .
Problem : The pageOut method is programmed as follows:
Mapped::pageOut(Page * p)
f p->flushMMU(); p->writeToDisk();
return Unmapped::Instance(); g;
We might derive a class WMapped from the class Mapped, hoping to reuse Mapped::pageOut . But now there is a problem: where Mapped::pageOut returns
Unmapped::Instance , WMapped::pageOut must return WUnmapped::Instance . Although behavior in the two states is similar, the state transitions dier for the COW
machine. If we attempt to reuse Mapped::pageOut by redening Unmapped::Instance
to return WUnmapped::Instance , we nd that RMapped::pageOut cannot reuse Mapped::pageOut ,
as it must return RUnmapped::Instance .
Solution : We use indirection to resolve the problem. Return the next state indirectly through a table of states called StateMap. That is, pageOut is programmed
as follows:
Mapped::pageOut(Page * p)
f p->flushMMU(); p->writeToDisk();
return map->Unmapped(); g;
101
Now, both WMapped and RMapped are derived from class Mapped, but the map
variable is initialized dierently in the two classes. The map in WMapped returns
WUnmapped for the invocation map->Unmapped(). In RMapped, it returns RUnmapped instead.
The general principle is that the StateMap together with the implicit virtual function table (VTable [Str91]) for each state object, expresses the relationships between
state transitions of the base and derived machines. Class WMapped is derived from
Mapped, while its map is initialized to return WMapped and WUnmapped. Thus,
state transitions from Mapped to Unmapped in the base machine map to transitions
from WMapped to WUnmapped.
Consequences : New state machines can be derived from old state machines in a
systematic way. Actions from base state machines can be reused in the derived
machine. But initializing the StateMaps is tedious. In our system, we solve this
problem by dening a small language to express the relationship between base and
derived machines.
The technique of using StateMaps can be easily extended to implement composition and
delegation between state machines. In the virtual memory system, we use composition
extensively to implement distributed shared memory consistency protocols. We briey
present and example, and comment on the implementation. Other features of objectoriented state machines are presented in [SC95].
102
DMapped
pageOut
pageAccess
pageAccess remAccess
Remote
DUnmapped
getPage
herePage
Null
Quiescent
Send
herePage
ackPage
Figure 4.4: DSM-NET State Machine
Figure 4.3: DSM-VM State Machine
MappedQ
Fetch
pageOut
herePage
pageAccess
remAccess
FetchN
SendN
pageAccess
ackPage
UnmappedQ
RemoteQ
Figure 4.5: DSM Composite State Machine
4.5.4.3 Composing State Machines
State machines are composed to combine behaviors dened in component machines.
We demonstrate composition by constructing a distributed shared memory (DSM) protocol machine (Figure 4.5) out of a virtual memory machine (Figure 4.3) and a networking
machine (Figure 4.4). DSM [SMC90] provides the illusion of a global shared address
space over networks of workstations, whose local memories are used as \caches" of the
global address space. The caches have to be kept consistent: a simple approach allows
only one machine to access a shared page at a time. If another machine attempts to
access that page, its virtual memory hardware intercepts the access, and its fault handler
fetches the page from the current page-owner. Thus, behavior for DSM has VM and networking aspects. We dene VM and networking behavior using separate state machines,
and compose them to get a DSM machine.
103
DSM-VM Machine: In a DSM system, pages may be either DMapped, DUnmapped or
Remote (Figure 4.3). The DMapped and DUnmapped states are inherited from the
simple VM machine (Figure 4.1). State Remote represents a page on some remote
machine, and remAccess denes actions for pages accessed by a remote machine.
The transitions are dened as though no networking were necessary. (The special
state Null ignores all VM actions; it is used in composition. We always create Null
and Error states for every state machine.)
DSM-NET Machine: The networking machine (Figure 4.4) implements a trivial pro-
tocol that sends a page to a remote machine, or gets one from a remote machine.
It handles details like fragmentation and sequence numbering.
DSM Machine: In the composite DSM Machine (Figure 4.5), sux Q indicates that
in the composite state, the DSM-Net state is Quiescent, and sux N indicates that
the VM state is Null. We implement transitions to and from RemoteQ using the
networking machine. MappedQ and UnmappedQ inherit behavior from the DSM-
VM machine.
Methods of the composite machine reuse behavior dened in methods of component
machines by initializing StateMaps so that the states returned from component methods
are actually composite states.
For example, in the DSM-NET machine, Quiescent::herePage returns the NET state
Send, but when invoked from the composite state MappedQ, it returns the composite
state SendN. In turn, ackPage when invoked from SendN, returns RemoteQ instead of
Quiescent. RemoteQ later gets used as a VM state.
4.5.5 Implementing Remote Interactions
The logic of consistency protocols for distributed shared memory is implemented using
state machines. But in addition to the logic, the virtual memory system has to interact
with dierent machines.
104
Consider the simple consistency protocol depicted in Figure 4.5. When there is a
pagefault, the pagefault operation visits objects like Domain, MemoryObject, MemoryObjectCache, and eventually invokes pageFault on some state object. If the page resides on
a remote machine, Remote::pageFault is invoked. It must determine the remote machine
that has the page (and is in state DMapped), contact it with the identiers for the virtual
page (i.e., memory object, oset within the object) retrieve the data, and update the
local virtual memory data structures. It must suspend processing when waiting for the
data, and proceed after the page is retrieved across the network.
By virtue of the internal structure of our system, all the necessary information to
locate the remote page, retrieve data, and operate on the local data structures is contained
in the Parameter object associated with the operation. Therefore, we can implement the
remote interaction by transmitting the parameter object to the remote machine. We
ensure that the reply also contains the parameter object, together with the contents
of the page, so that we can simply continue processing when the reply arrives. Thus,
remote interactions t in smoothly with our basic structure. The key design decisions
are examined below in greater detail.
4.5.5.1 Continuations
First we show how to use continuations for ecient remote interactions.
Context : Consider the usual way of implementing the execution of pagefaults in a
distributed shared memory system. When the user process faults, a thread in the
kernel begins pagefault processing by executing the methods of the OpPageFault.
Eventually, the operation requires page contents that are on a remote system. The
thread initiates a remote request and blocks waiting for the reply. On the remote
side, some server thread picks up the request. That thread must retrieve the page
contents, either from memory or from disk if the page has been paged out. There
may be other operations demanded by the memory consistency protocol.
105
Problem : An operating system that supports networking, distributed le systems,
distributed shared memory, supports considerable concurrent processing and may
require many threads. But threads are operating system resources managed by the
kernel. They contain the execution stack, data for schedulers, and tie up slots in
kernel data structures. Therefore threads are too expensive to be left waiting for
activities to complete.
Furthermore, suppose there are concurrent pagefaults such that pagefault processing can be \batched" together (for instance, faults on adjacent pages from dierent
processes). If a thread is dedicated to an operation, the threads for the two operation will block separately; batching could be implemented in some ad-hoc fashion
at some network layer. The issue is that the system has knowledge about memory
operations (as opposed to generic threads) so that operations can be intelligently
scheduled in ways dierent from generic thread scheduling. To exploit this knowledge, we should dissociate threads from operations.
Solution : Instead of threads, use Parameter objects to implement continuations. A
continuation [AS96] at any point of execution informs how to continue the processing. It is an object that contains all the necessary data and a pointer to the code
that continues the operation.
In our architecture, Parameter objects contain all the data necessary for a virtual
memory operation. We add one more parameter to use it as a continuation.
Consider an operation like pagefault that starts on one machine, waits until a
remote reply is received, and continues processing. We divide the operation into
two methods, one for use prior to page fetching, another for use after the page is
received. The Parameter object for the operation has a variable that points to the
second method. When the rst method is completed, processing can be continued
given just the Parameter object.
When a thread T that executes the operation completes the rst method, it adds
the Parameter object queue, schedules a remote request, and may then pick up any
106
other task. When enough replies are received, the networking driver (or a thread
dedicated to reply processing) will schedule a thread U to pick up where T left o.
A similar scheme can be used on the remote machine to process incoming requests.
Consequences : Continuations do not consume slots in kernel structures, so they
are cheap to create and destroy. As fewer threads are used, scheduling and context
switching overhead is minimized. Based on the data in Parameter object, operations
can be batched or redundant operations eliminated.
A drawback is that all operations have to be divided into articial methods, unless
the implementation language supports the creation and use of continuations.
Next we consider message demultiplexing.
4.5.5.2 Active Messages
Ecient message demultiplexing is achieved by using Parameter objects as active messages [vECGS92].
Context : A messages arrives through network drivers as a chunk of uninterpreted
data. The receiver has to interpret the data, and take any action requested. In
case of distributed shared memory, the messages are typed, the address space and
the memory object identiers are included. The types indicate the desired action,
an the object identiers indicate the data structures that are aected. When the
remote request is complete, the originator gets a reply. The reply also contains
identiers that allow it to be matched with the request.
Problem : The usual way of interpreting a message uses some form of table lookup
to get at the action requested by the message and the data structures to be aected.
Table lookups can be expensive, especially because the tables are often protected
by semaphores that serialize concurrent accesses.
Solution : We can avoid interpretation by embedding directly the pointers to meth-
ods and objects in the message. For example, instead of having to interpret message
107
types and decide the action, the program counter for the action code can reside in
the message. If the networked computers are of the same type, and the action is
described by kernel code that always resides at the same address, then the receiving thread can immediately jump to that address. If the action and data structure
addresses dier, they can be determined at some prior time (e.g., as part of setup)
Thus, Parameter objects that contain the continuation information can also serve
as messages.
A message received from the network is usually in a data format resulting from the
serialization of data into a sequence of bytes. If the machine formats are dierent
from the network format, the data has to be interpreted. We can reduce the
interpretation overhead by wrapping the uninterpreted data in an object that has
the same interface as a Parameter, but interprets the raw data on demand, when
an Operation queries or sets parameters.
Consequences : When many messages arrive at the network device driver, it has to
determine the recipient. Active messages hasten this demultiplexing.
4.5.6 Dynamic Page Distribution
In the conventional architecture, operating system pages are distributed among various
subsystems and MemoryObjectCaches. Some pages are permanently allocated: for instance, pages for kernel data structures. Other subsystems such as the process system
or the le system may request and return pages dynamically. But most pages are dynamically allocated by MemoryObjectCaches from a Store that manages physical pages.
A pageout daemon watches the Store to detect excess allocation and periodically visits
MemoryObjectCaches to preempt pages. MemoryObjectCaches dene policies for selecting
least desirable pages that are given up when requested by the pageout daemon. But page
preemption can be expensive, because it may conict with pagein. Similarly, dynamic
page allocation can be expensive in systems like network drivers where it is undesirable for
the driver to pause instead of delivering trac. Resource management overhead can be
108
reduced by spreading it over regular computations. The Resource Exchanger [SC96]
is used in our architecture to make resource management more ecient.
Context : There are dynamically allocated resources like pages used by dierent
allocators in a operating system. The particular resource used by an allocator is
not important, only the quantity matters. The resources can be preempted from
an allocator if necessary. We want to avoid unfair distribution of resources among
allocators; at the same time, some allocators may have a greater claim than others.
The resources should be distributed according to need.
Problem : A common approach to managing preemptable dynamic resources is to
run a daemon process that reclaims them from the allocators. However, this means
that an allocator that needs a resource may have to wait while preemption is occurring. Also, preemption may cause allocators to suspend operations until preemption
is completed. Such pauses are often unacceptable.
Solution : We interleave allocation and deallocation of resources, so that the allocator is not drained of resources unless absolutely necessary.
For example, consider a network driver that uses pages to receive messages. After
a message is received, it must hand the page over to some server for processing: for
instance, assembling fragments. While the page is being used, if the drivers page
pool drains of pages, it may need to allocate new pages, wasting time needed for
communication. We can avoid this if instead of giving up the page to the server,
the driver exchanges a page with the server. Thus, allocation and deallocation are
interleaved. This means that the server needs to preallocate at least one page.
Multiple servers may interact with the driver in this manner. Servers maintain
their own pools of pages ready to exchange with the driver. If a server expects
bursty trac, it preallocates pages. The number of pages given to a server depends
on its credit with the memory system. The credit may be preassigned: for example
a video server would have greater credit than a audio server; or it may vary. If a
109
server runs out of credit and buers, then the driver drops server packets, throttling
resource hogs.
MemoryObjectCaches also use a similar scheme. Every cache has a credit for a
number of pages. During pagefault processing, the cache picks pages that can be
returned if it is approaching the credit limit. If page contents need not be saved to
backing store, it may reuse the page internally; otherwise, the page will be returned
to Store. The decit number of pages are allocated from the Store.
Consequences : Resource exchange ameliorates the need for resource preemption. It
also reduces the time an allocator may have to wait for a resource. But resource exchange means that we must have enough resources that the allocators must have at
least one resource to exchange. Otherwise, we must accept a standard preemption
scheme.
4.6 Summary
This chapter described a new architecture for building virtual memory systems. The
architecture has the following primary attributes.
It separates data structures from operations over data structures. Basic objects
of a virtual memory system implement various types of tables. Typical operations
manipulate the objects in groups. Expressing these manipulations in a centralized
manner makes it easier to understand them. By the same token, it becomes easier
to evaluate the eects of new operations and data structures.
It reies operations and operation parameters into objects. This makes the operations and their eects explicit. Also, the reied objects can be used as continuations to program remote interactions without overuse of threads. Parameter
objects also serve as active messages for fast remote interactions. The operation
objects make the order of basic object invocation explicit, so that it is easier to
110
verify the correctness of concurrency control. Moreover, parameter objects can be
used as concurrency control tokens.
It uses object-oriented state machines to program the logic of the operations. There-
fore, code for new operations can be incrementally added by inheritance and composition, dramatically improving code reuse. Structuring operations as state machines
also makes the logic easier to understand.
It improves resource management by spreading resource allocation and deallocation
during computations. As a result, the need for preemption is reduced.
Experience with the Choices system has proven the worth of the architecture. We began with a virtual memory system without copy-on-write support and rudimentary distributed shared memory. The system with new architecture, with copy-on-write support
and dierent consistency protocols was 30% smaller without loss of performance.
111
Chapter 5
Conclusion
This thesis is concerned with the development of a theoretical basis and a practical architecture for building distributed systems. Our example has been the development of
distributed shared memory protocols. We have developed a new protocol design method,
novel distributed shared memory protocols, and a exible architecture for object-oriented
systems that support concurrent operations on groups of objects, and interact with remote systems. The architecture has been used to implement a virtual memory system
that supports distributed shared memory.
5.1 Summary
In Chapter 2, we presented a method for synthesizing process coordination protocols.
Our method shows how to structure the design trajectory for protocols. We begin with
high-level protocols that use abstract communication operators. These protocols are easy
to analyze, so system wide verication is conducted at this level. The next step is to develop implementations of the abstract communication operators. These implementations
are developed in a notation that hides the details of communication media, but allows
the designer to express how a process can control the execution of another process. We
presented conditions that these operator implementations must obey so that they can be
composed to implement the full protocol. Because of the condition, the protocol imple112
mentation is guaranteed to replicate the behaviors specied in the original specication.
The last step is to translate the second-level implementations into a formalism that can
be easily implemented using shared memory or message passing programs. The form
of the third-level implementations ensures that their composition also implements the
original specications correctly.
At each level, the protocols and subprotocols we encounter have small state spaces.
The original specication is succinct due to its abstractness, while the successive steps
look only at parts of the original protocol. As a result, verication tools that use exhaustive search are eective in validating the protocols.
The implementations of the abstract communication operators constitute a standard
library that can be used in future protocol designs.
Thus, we have developed the basis for an eective method for synthesizing protocols.
In Chapter 3, we developed consistency protocols that implement an ecient distributed shared memory for computers connected with wide-area interconnects. We
showed that communication related to synchronization makes it dicult to use distributed shared memory when the communication latency is high. This is because processes contend with one another for access to synchronization data structures. We can
reduce this contention if we can anticipate requests by processes for the data computed
within a synchronization construct. The performance results showed that this approach
results in good performance over wide area networks.
We also showed how our design method can help guide the development of the protocols by analyzing protocols at various levels of detail.
In Chapter 4, we presented a software architecture used to develop a virtual memory
system that supports our distributed shared memory protocols. But the architecture is
considerably more general, in that it can be applied wherever object-oriented systems
involve concurrent operations over groups of objects. We showed how such systems
can be designed so that the objects, operations, and synchronization aspects can be
113
separated. This separation means that adding new objects and new operations is easier,
because the relationships between objects and the concurrency control code is explicit.
We demonstrated how the operations can be extended smoothly using continuations so
that they can aect objects on remote machines. Another feature of the architecture
is the use of object-oriented state machines that allow complex, state-based logic to be
structured to increase reuse and permit systematic extensions.
In summation, this research has resulted in improvements in protocol design and implementation techniques. We have also shown that distributed shared memory can be
useful over wide-area networks.
5.2 Future Research
In this thesis, we have barely begun the development of the protocol synthesis method.
Future research is need to extend the power of our specication language, and experience
is needed to determine the evolution of our notations. Verication tools have to be
adapted so that our models can be analyzed. A library of standard replacements also
needs to be developed. Another direction is to adapt our approach to notations like
LOTOS.
Distributed shared memory consistency protocols might prove to be useful for maintaining consistency over Web documents and other distributed data. A standard library
for distributed shared memory can be developed, much like the MPI and PVM message
passing libraries.
We believe that the software architecture we have proposed is useful for applications
such as workow. The principles developed for the architecture, like object-oriented state
machines, the systematic use of rst-class representations for operations, and the use of
continuations can be applied to improve operating system design.
114
Bibliography
[AF92]
Hagit Attiya and Roy Friedman. A correctness condition for highperformance multiprocessors. In Proceedings of the 24th ACM Symposium
on the Theory of Computing, pages 679{690, 1992.
[AHJ91]
Mustaque Ahamad, Phillip W Hutto, and Ranjit John. Implementing and
programming causal distributed shared memory. In Proceedings of the 11th
International Conference on Distributed Computing Systems, pages 274{281,
May 1991.
[AHN+93] Mustaque Ahamad, Phillip W. Hutto, Gil Neiger, James E. Burns, and
Prince Kohli. Causal memory: Denitions, implementation and programming. Technical Report GIT-CC-93/55, Georgia Institute of Technology,
1993.
[Alp86]
Bowen Alpern. Proving Temporal Properties of Concurrent Programs: A
Non-Temporal Approach. PhD thesis, Cornell University, February 1986.
[And90]
Thomas E. Anderson. The performance of spin-lock alternatives for sharedmemory multiprocessors. IEEE Transactions on Parallel and Distributed
Systems, 1(1):6{16, January 1990.
[AS96]
Harold Abelson and Gerald Jay Sussman. Structure and Interpretation of
Computer Programs. M.I.T. Press, Cambridge, Mass, 1996.
115
[BvdLV95] Tommaso Bolognesi, Jeroen van de Lagemaat, and Chris Vissers. LOTOSphere: software development with LOTOS. Kluwer Academic Publishers,
1995.
[BZ83]
Daniel Brand and Pitro Zaropulo. On communicating nite state machines.
Journal of the ACM, 30(2):323{342, April 1983.
[BZS93]
Brian N. Bershad, Matthew Zekauskas, and Wayne A. Sawdon. The midway
distributed shared memory system. In IEEE Computer Society International
Conference, pages 528{537, 1993.
[Cam74]
R.H. Campbell. The Specication of process synchronization by PathExpressions. In Lecture Notes in Computer Science, pages 89{102, 1974.
[Cam76]
Roy Harold Campbell. Path Expressions: A technique for specifying process
synchronization. PhD thesis, University of Newcastle Upon Tyne, August
1976.
[CBZ95]
John B. Carter, John K. Bennet, and Willy Zwaenepoel. Techniques for
reducing consistency-related communication in distributed shared memory
systems. ACM Transactions on Computer Systems, 1995. To appear.
[CES86]
E. M. Clarke, E. A. Emerson, and A. P. Sistla. Automatic verication of
nite-state concurrent systems using temporal logic specications. ACM
Transactions on Programming Languages and Systems, 8(2):244{263, April
1986.
[CM86]
K. M. Chandy and J. Misra. How processes learn. Distributed Computing,
1:40{52, 1986.
[DCM+90] Partha Dasgupta, R. C. Chen, S. Menon, M. Pearson, R. Ananthnarayanan,
M. Ahamad, R. Leblanc, W. Applebe, J. M. Bernabeu-Auban, P. W. Hutto,
M. Y. A. Khalidi, and C. J. Wilenkloh. The design and implementation
116
of the Clouds distributed operating system. Computing Systems Journal,
Winter 1990.
[Dil96]
David L. Dill. The mur' verication system. In 8th International Conference
on Computer Aided Verication, pages 390{393, July/August 1996.
[DKCZ93] Sandhya Dwarkadas, Pete Keleher, Alan L. Cox, and Willy Zwaenepoel.
Evaluation of release consistent software distributed shared memory on
emerging network technology. In Proceedings of the 20th International Symposium on Computer Architecture, 1993.
[DSB86a] Michael Dubois, Christoph Scheurich, and Faye Briggs. Memory access dependencies in shared-memory multiprocessors. In International Symposium
on Computer Architecture, pages 434{442, May 1986.
[DSB86b] Michael Dubois, Christoph Scheurich, and Faye Briggs. Memory access dependencies in shared-memory multiprocessors. In International Symposium
on Computer Architecture, pages 434{442, May 1986.
[FHMV95] Ronald Fagin, Joseph Halpern, Yoram Moses, and Moshe Vardi. Knowledgebased programs. In Proceedings of the 14th ACM Symposium on Principles
of Distributed Computing, pages 129{143. Association for Computing Machinery, ACM Press, 1995.
[FLP85]
M. J. Fischer, N. A. Lynch, and M. S. Paterson. Impossibility of distributed
consensus with one faulty processor. Journal of the ACM, 32(2):374{382,
April 1985.
[FLR+94] Babak Falsa, Alvin R. Leibeck, Steven K. Reinhardt, Iannis Schoinas,
Mark D. Hill, James R. Larus, Anne Rogers, and David A. Wood.
Application-specic protocols for user-level shared memory. In Supercomputing 94, 1994.
117
[FP89]
Brett Fleisch and Gerald Popek. Mirage: A coherent distributed shared
memory design. In ACM Symposium on Operating System Principles, 211223, 1989.
[Gab87]
Dov Gabbay. Modal and temporal logic programming. In Antony Galton,
editor, Temporal Logics and Their Applications, chapter 6, pages 197{237.
Academic Press, New York, 1987.
[GH85]
M. G. Gouda and J. Y. Han. Protocol validation by fair progress state
exploration. Computer Networks and ISDN Systems, 9:353{361, 1985.
[GHJV93] Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides. Design
patterns: Abstraction and reuse of object-oriented design. In Proceedings
of the European Conference on Object-Oriented Programming, number 707
in Lecture Notes in Computer Science, pages 406{431. Springer-Verlag, New
York, 1993.
[GHP92]
P. Godefroid, G.J. Holzmann, and D. Pirottin. State space caching revisited.
In Proc. 4th Computer Aided Verication Workshop, Montreal, Canada, June
1992. also in: Formal Methods in System Design, Kluwer, Nov. 1995, 1-15.
[GLL+90] K. Gharachorloo, D. Lenoski, J. Laudon, P.Gibbons, A. Gupta, and J. Hennessy. Memory consistency and event ordering in scalable shared memory
multiprocessors. In Proceedings of the 17th International Symposium on
Computer Architecture, 1990.
[GW94]
Patrice Godefroid and Pierre Wolper. A partial approach to model checking.
Information and Computation, 110(2):305{326, May 1994.
[GY84]
M. G. Gouda and Y. T. Yu. Protocol validation by maximal progress exploration. IEEE Transactions on Communications, COM-32(1):94{97, 1984.
[HFM88]
D. Hensgen, R. Finkel, and Udi Manber. Two algorithms for barrier synchronization. International Journal of Parallel Programming, January 1988.
118
[HM90]
Joseph Y. Halpern and Yoram Moses. Knowledge and common knowledge in
a distributed environment. Journal of the ACM, 37(3):549{587, July 1990.
Also in Proceedings of the 4th ACM Symposium on Principles of Distributed
Computing(1984).
[Hol91]
Gerard J. Holzmann. Design and validation of computer protocols. Prentice
Hall, Englewood Clis, New Jersey, 1991.
[HU79]
John E. Hopcroft and Jerey D. Ullman. Introduction to Automata Theory, Languages, and Computation. Addison-Wesley Publishing Company,
Reading, Massachusetts, 1979.
[Hu95]
Alan John Hu. Techniques for Ecient Formal Verication Using Binary
Decision Diagrams. PhD thesis, Stanford University, December 1995.
[HZ87]
Joseph Y. Halpern and Lenore D. Zuck. A little knowledge goes a long
way: Simple knowledge-based derivations and correctness proofs for a family
of protocols. In ACM Symposium on Principles of Distributed Computing,
pages 269{280. ACM, 1987.
[Ip96]
Chung-Wah Norris Ip. State Reduction Methods for Automatic Formal Verication. PhD thesis, Stanford University, December 1996.
[JA94]
Ranjit John and Mustaque Ahamad. Evaluation of causal distributed shared
memory for data-race-free programs. Technical Report GIT-CC-94/34, Georgia Institute of Technology, 1994.
[JKW95]
Kirk L. Johnson, M. Frans Kaashoek, and Deborah A. Wallach. Crl: Highperformance all-software distributed shared memory. In ACM Symposium
on Operating System Principles, 1995.
[JR91]
Ralph E. Johnson and Vincent F. Russo. Reusing object-oriented designs. Technical Report UIUCDCS-91-1696, University of Illinois at UrbanaChampaign, May 1991.
119
[KHvB92] Christian Kant, Teruo Higashino, and Gregor von Bochmann. Deriving protocol specications from service specication written in LOTOS. Technical
Report 805, Universite de Montreal, January 1992.
[KN93]
Yousef A. Khalidi and Michael N. Nelson. The Spring virtual memory system.
Technical Report TR-93-9, Sun Microsystems, February 1993.
[Kur94]
Robert P. Kurshan. Computer-aided verication of coordinating processes :
the automata-theoretic approach. Princeton University Press, 1994.
[LAA87]
M. C. Loui and H. H. Abu-Amara. Memory requirements for agreement
among unreliable asynchronous processes. Advances in Computing Research,
4:163{183, 1987.
[Lam79]
Leslie Lamport. How to make a multiprocessor computer that correctly
executes multiprocess programs. IEEE Transactions on Computers, C28(9):690{691, September 1979.
[Lam94]
Leslie Lamport. The temporal logic of actions. ACM Transactions on Programming Languages and Systems, 16(3):872{923, May 1994.
[LH89]
Kai Li and Paul Hudak. Memory coherence in shared virtual memory systems. ACM Transactions on Computer Systems, 7(4):321{359, November
1989.
[Lim95]
Swee Boon Lim. Adaptive Caching in a Distributed File System. PhD thesis,
University of Illinois at Urbana-Champaign, 1995.
[LM95]
Hong Liu and Raymond E. Miller. Generalized fair reachability analysis
for cyclic protocols with nondeterminism and internal transitions. Technical Report UMCP-CSD:CS-TR-3422, University of Maryland, College Park,
February 1995.
120
[Lon93]
David E. Long. Model Checking, Abstraction and Compositional Verication.
PhD thesis, Carnegie Mellon University, July 1993.
[LS88]
Richard J. Lipton and Jonathan S. Sandberg. Pram: A scalable shared
memory. Technical Report CS-TR-180-88, Princeton University, 1988.
[McM92]
Ken McMillan. Symbolic Model Checking: An Approach to the State Explosion Problem. PhD thesis, Carnegie Mellon University, 1992.
[MF90]
Ronald G. Minnich and David J. Farber. Reducing host load, network load
and latency in a distributed shared memory. In International Conference on
Distributed Computing Systems, 1990.
[MP91]
Zohar Manna and Amir Pnueli. The Temporal Logic of Reactive and Concurrent Systems, volume 1. Specication. Springer-Verlag, New York, 1991.
[MW84]
Zohar Manna and Pierre Wolper. Synthesis of communicating processes from
temporal logic specications. ACM Transactions on Programming Languages
and Systems, 6(1):68{93, January 1984.
[PD97]
Fong Pong and Michel Dubois. Verication techniques for cache coherence
protocols. ACM Computing Surveys, 29(1):82{126, March 1997.
[Pon95]
Fong Pong. Symbolic State Model: A New Approach for the Verication of
Cache Coherence Protocols. PhD thesis, University of Southern California,
1995.
[Pos81]
Jon Postel. Transmission control protocol. Internet RFC 793, Sep 1981.
[PS91]
Robert. L. Probert and Kassim Saleh. Synthesis of communication protocols:
Survey and assessment. IEEE Transactions on Computers, 40(4):468{476,
April 1991.
121
[RJY+87] Richard Rashid, Avadis Tevanian Jr., Michael Young, David Golub, Robert
Baron, David Black, William Bolosky, and Jonathan Chew. Machineindependent virtual memory management for paged uniprocessors and multiprocessor architectures. In Proceedings of the 2nd International Conference
on Architectural Support for Programming Languages and Operating Systems,
pages 31{39, 1987.
[Rus91]
Vincent Frank Russo. An Object-Oriented Operating System. PhD thesis,
University of Illinois at Urbana-Champaign, 1991.
[SC95]
Aamod Sane and Roy H. Campbell. Object-oriented state machines: Subclassing, composition, delegation and genericity. In Proceedings of the Conference on Object-Oriented Programming Systems, Languages and Applications (OOPSLA'95), pages 17{32, October 1995.
[SC96]
Aamod Sane and Roy Campbell. Resource exchanger: A behavioral pattern
for low overhead concurrent resource management. In Pattern Languages
of Program Design. Addison-Wesley Publishing Company, Reading, Massachusetts, 1996. (To appear).
[SMC90]
Aamod Sane, Ken MacGregor, and Roy Campbell. Distributed virtual memory consistency protocols: Design and performance. In Second IEEE workshop on Experimental Distributed Systems, 1990.
[Str91]
Bjarne Stroustrup. The C++ Programming Language. Addison-Wesley Publishing Company, Reading, Massachusetts, 2 edition, 1991.
[Tan92]
Andrew S. Tanenbaum. Modern Operating Systems. Prentice Hall, Englewood Clis, New Jersey, 1992.
[vECGS92] Thorsten von Eicken, D. E. Culler, S. C. Goldstein, and K. E. Schauser.
Active messages: a mechanism for integrated communication and compu122
tation. In Proceedings of the 19th International Symposium on Computer
Architecture, May 1992.
[VSvSB91] Chris A. Vissers, Giuseppe Scollo, Marten van Sinderen, and Ed Brinksma.
Specication styles in distributed systems design and verication. Theoretical
Computer Science, 89(1):179{206, October 1991.
[Wes78]
Colin H. West. General technique for communications protocol validation.
IBM Journal of Research and Development, 22(3):393{404, 1978.
[WG93]
P. Wolper and P. Godefroid. Partial-order methods for temporal verication.
In Proc. CONCUR '93, volume 715 of Lecture Notes in Computer Science,
pages 233{246, Hildesheim, August 1993. Springer-Verlag.
[WL93]
Pierre Wolper and Denis Leroy. Reliable hashing without collision detection.
In 5th International Conference on Computer Aided Verication, number
697 in Lecture Notes in Computer Science, June 1993.
123