The DisCo Language and Temporal Logic of Actions 1 ... - CiteSeerX

16 downloads 0 Views 45KB Size Report
In terms of TLA program variables we can interpret DisCo class declarations as ..... es of such features are unboundedly nondeterministic action parameters, fairness condi- ... However, because of the facility for action parameters, no generality.
The DisCo Language and Temporal Logic of Actions (September 1990) Hannu-Matti Järvinen and Reino Kurki-Suonio [email protected], [email protected] Tampere University of Technology Software Systems Laboratory Box 527, SF-33101 Tampere, Finland Abstract DisCo is a specification language for reactive systems, based on the execution model of joint actions. Its main constructs for safety specification are described in this paper in terms of temporal logic of actions. The principles of such a translation are relatively straightforward, and DisCo can therefore be thought of as a language for constructing specifications in this logic. Refinement and modular combination of specifications are supported by constructions that are less general than what would be semantically necessary, but their correctness is syntactically guaranteed, and they are suited for developing design-oriented, executable specifications. Keywords: executable specifications, joint actions, modularity, reactive systems, superposition

1. Introduction The notion of joint action systems was developed in [BK83, BK84, BK88a, BK88b] to describe distributed systems and to support a rigorous methodology for their design. Based on this work and on case studies, an experimental language DisCo (Distributed Cooperation) was designed for the specification and design of reactive systems [KK88, KJ89, JK90a, JKSS90, Ku90]. Examples of related approaches are Unity [CM88] and the relational notation of [LS90]. Unlike in Unity, no special logic or proof system was proposed for joint action systems. Instead, the ideas were formulated in such a way that temporal logic [MP83, Pn86] could be applied. Later, the joint action approach has also been investigated in the framework of refinement calculus [Ba90, BK90]. Although well suited for terminating distributed computations, this framework has drawbacks in connection with reactive systems, where termination is basically indistinguishable from infinite stuttering. Recently, Lamport has put forward a temporal logic of actions [La90], which offers a simple alternative for reasoning about reactive systems. In this paper we investigate how DisCo relates to this logic, i.e., how DisCo can be used as a language for constructing systems that are understood in terms of this logic. For simplicity, we consider safety properties only and omit liveness. Like programming languages, DisCo has notions that help the designer to express his intentions in a way that supports the construction of specifications and improves their readability. In terms of logic these introduce simple invariants and theorems, the proofs of which are embedded in syntax (or static semantics, as it is sometimes called). Typical examples are data types, structuring of DisCo objects, and preservation of safety properties in modular combination of systems.

-2-

On the other hand, a language may be restrictive in disallowing certain constructions that would be easy to express in logics. Such restrictions are found to be small in DisCo, and they can be justified by the goal to construct design-oriented, executable specifications. The structure of the paper is as follows. Safety specification in temporal logic of actions is briefly introduced in Section 2, and in Section 3 we outline how DisCo specifications can be understood in these terms. An example that illustrates this correspondence and the modularity constructs of DisCo is given in Section 4; a more general discussion of the topics is postponed to Section 5. The paper ends with concluding remarks in Section 6.

2. Safety Specification in Temporal Logic of Actions Temporal logic of actions (TLA) [La90] deals with behaviors σ, defined as infinite sequences of states, σ = (s0, s1, ...). Each state si is a collection of values of so-called program variables, denoted collectively by u in the following. A pair of consecutive states (si, si+1) in a behavior is called a transition. If v denotes a subset of program variables, and states si and si+1 do not differ in the values of v, transition (si, si+1) is called a v stuttering transition. A state predicate P(u), which involves program variables u, is true for a behavior σ if it holds when u is replaced by the values of the program variables in the initial state s0 of σ. The temporal logic formula ❏P(u) is defined to be true if P(u) holds for all suffixes of σ, including σ itself. An action A(u, u') is a boolean-valued expression containing both primed and unprimed state variables. Transition (si, si+1) is an A transition if A(u, u') holds when u and u' are replaced by the values of the program variables in states si and si+1, respectively. If v is a subset of u, and A(v, v') is an action, then ❏[A]v is defined to be true for a behavior σ, if each transition in σ is either an A transition or a v stuttering transition. (If A involves only unprimed program variables, this reduces to ❏A for the state predicate A.) The specification of a reactive system can now be given as a formula that restricts the behaviors that the system is allowed to generate. A safety specification Φ is a formula Φ = INIT ∧ ❏[A]u that allows any behavior where the initial state satisfies the state predicate INIT, and all transitions are either A transitions or u stuttering transitions. Since no liveness conditions are given, any finite prefix of a behavior satisfying Φ can be completed by infinite u stuttering to another behavior that also satisfies Φ. Satisfaction of Φ requires a system to possess the state variables u of Φ. In addition it may have other state variables with arbitrarily changing values. (For simplicity we ignore the situation where internal program variables are hidden in Φ by quantification, as defined in [AL88].) To allow easier manipulation of the action part A of Φ, it is often given as a disjunction of a collection of individual actions, A = ∨i Ai.

3. Specifications in DisCo DisCo specifications deal with similar infinite behaviors as TLA. Therefore, their meaning can be easily described in terms of this logic. For simplicity we omit the liveness part of also DisCo.

-3-

3.1. System State System state is partitioned in DisCo into a collection of local states of objects, each object x being an instantiation of some class c, x ∈ c. No global naming is provided for objects, which can therefore be identified only on the basis of their classes and local states. Classes are declared and named individually, and hence their number is finite. All objects are assumed to be instantiated in the initial state. Instead of explicit initialization, legal initial states are determined by initial conditions. When a DisCo specification is executed, a concrete initialization is given that has to satisfy these initial conditions. For each class the number of objects is not allowed to change within an execution. This imposes no essential restriction, since the number of objects need not be finite. The local states of objects are structured as hierarchical finite-state systems, with variables associated with states. To be more specific, the name of a class denotes a unique outermost state in which objects of this class always reside, and, for each state one can give zero or more declarations for variables and internal states. Each state declaration introduces a finite collection of mutually exclusive alternatives. When several state declarations are given within the same state, they introduce parallel finite-state systems. In terms of TLA program variables we can interpret DisCo class declarations as follows. Each state declaration introduces an array of program variables u[i], where the name u uniquely identifies the place of the declaration within the state hierarchy, i is an index that identifies an object of the associated class, and the type of u[i] is an enumeration type ranging over the alternative state names in the declaration. (The range indc of index i for a class c is determined by the initial state.) Similarly, each variable declaration in DisCo introduces an array of program variables u[i] of the types of these declarations, with u identifying the place and variable name of the declaration. Obviously, any state predicate p in DisCo, referencing the local states of some objects, can be transliterated into a TLA state predicate P involving the corresponding program variables. (We adopt the convention of using lower and upper case letters to denote corresponding entities in DisCo and TLA.) In particular, the initial conditions of a DisCo system can be transliterated into an initial state predicate INIT. Compared to this interpretation in terms of TLA program variables, the syntactic form of DisCo class declarations also introduces a number of invariants for objects. For instance, DisCo variables are always typed, and an object cannot be in an internal state unless it is also in the state within which this internal state has been declared. Furthermore, each reference to a variable introduces an implicit predicate about the object being in the associated state; this often allows more concise expression of conditions. 3.2. Actions Apart from syntactic details, each action a is given in DisCo in terms of optional parameters pi ∈ ti, i ∈ indp , a finite collection of participants xj ∈ cj, j ∈ indx , an enabling guard g, and a body b. Parameters pi are values that are specified by their types ti, participants xj are objects specified by their classes cj, the guard g is a predicate involving the parameters and the local states of the participants (and possibly also other parts of the system state by quantification over objects), and b is a sequence of assignments and state transition statements (which are conceptually also assignments) describing how the local states of the participants are modified. In TLA this corresponds to an action A of the form

-4A = ∀ i ∈ indp , j ∈ indx : ∃ pi ∈ ti , kj ∈ indcj : G ∧ B. Here G is a transliteration of the guard g, where each participant xj ∈ cj is identified by its index kj ∈ indcj. As the body b is a non-looping sequence of assignment statements, it determines a unique mapping from the old local states of the participants to their new states, which can be directly computed. Its transliteration into an expression with primed and unprimed program variables is denoted here by B. For a DisCo system with initial condition init and actions ai, the corresponding safety specification in TLA is then INIT ∧ ❏[∨i Ai]u , where INIT and Ai correspond to init and ai, respectively, and u is the collection of program variables. 3.3. Superposition Construction of modular DisCo specifications is associated with superposition, a technique with which a system is modified by extending its state and by transforming its actions to deal with these extensions. Action guards may also be strengthened in this connection. Superposition is supported by language facilities in DisCo, with static semantics that guarantees preservation of safety properties. Let Φ = INIT ∧ ❏[∨i Ai]u be the safety formula corresponding to a DisCo system of which a new system is to be derived by superposition. For simplicity we assume that a stuttering action is explicitly given as one of the actions Ai ; all other actions Ai are assumed to correspond to unique DisCo actions ai. DisCo language rules now guarantee that the formula for the system that results from superposition has the form Φ' = INIT ' ∧ ❏[∨i A'i]v , where INIT ' ⇒ INIT, u ⊆ v, and each Ai' is a disjunction ∨j A'i, j where A'i, j ⇒ Ai for all i and j. Obviously Φ' ⇒ Φ, i.e., all safety properties of the original system are preserved. Although the identity of actions is not significant in TLA, it plays a role in the DisCo rules that guarantee the correctness of superposition steps. Each A'i, j corresponds in the resulting DisCo system to a separate action a'i, j , which is called a refinement of ai. This refinement relation is syntactically determined, and a'i, j may have more parameters and participants than ai, its guard may be stronger, and its body may update state components that were not included in the original system. The new state components are either extensions to old objects or new objects belonging to new classes. 3.4. Combined Systems Structurally DisCo specifications are composed of modules. Modules may use other modules by importing them non-cyclically, and each module determines a complete specification. Classes of module M can be used in another module M' only if M has been imported (directly or indirectly) by M'. Otherwise, the same name denotes different things in different modules.

-5-

Upper

High

Lower

Mid

High_and_Mid

Figure 1. Derivation of a specification.

When more than one module is imported, naming of classes and actions is first made unique by explicit renaming. Then the systems are combined into a single system, which can subsequently be modified by superposition in the way described above. When systems are combined, the refinement history of their actions becomes significant. In order to describe this, some terminology is needed. If system S' has been obtained of S either by superposition or by combining S with some other systems, then S is called an immediate predecessor of S'. The general predecessor relation is the transitive closure of this. If action a' is obtained of a either by refinement (as described above) or by combining it with other actions (in a way to be described below), then a is called an immediate ancestor of a'. The general ancestor relation is the transitive closure of this. Two actions in different systems are called compatible, if all their ancestors in all common predecessor systems are the same, which is a syntactic property. Let Φj = INITj ∧ ❏[∨i Aji]uj , j = 1, ..., n be the safety formulae for n DisCo systems to be combined. We assume that the names of the state variables in Φj have already been made unique. We also assume that stuttering actions have been explicitly included in each formula Φj. The safety formula for the combined system is then Φ = INIT ∧ ❏[∨ (A1i1 ∧ ... ∧ Anin)]u , where INIT = ∧j INITj , u = ∪j uj , and indices i1, ..., in run through all combinations where the corresponding DisCo actions a1i1, ..., anin are mutually compatible. Each such combination A1i1 ∧ ... ∧ Anin corresponds in the combined DisCo system to a single action that has all a1i1, ..., anin as its ancestors. Notice that the weakest TLA formula that implies all component specifications Φj is ∧jΦj, which could be expressed similarly to Φ, with indices i1, ..., in running through all possible combinations. Obviously, Φ is in general stronger than this, Φ ⇒ ∧jΦj.

4. Example In this section we outline an example that has been considered in more detail elsewhere [JK90b]. Emphasis will here be on how DisCo systems are combined and refined.

-6-

enter_mid

exit_mid

sender_mid

receiver_mid

channel_mid send_mid

receive_mid

Figure 2. Objects and actions in the upper interface.

The example describes a simple protocol with a layered structure. The modularity in deriving this specification is illustrated in Figure 1, the lines and arrows indicating system combination and superposition. Upper is a module that describes communication at a level of abstraction where reliable communication can be assumed. Lower is an otherwise similar module, but its channels may also loose messages. High is an application module that makes use of the reliable communication services provided by Upper. Mid is an implementation of Upper by using the unreliable communication facilities of Lower. Finally, High_and_Mid is a module formed by combining both High and Mid; it describes the complete behavior of the application and the communication system, including both the underlying unreliable channels and the abstraction of the reliable channel. 4.1. Upper Interface Module Upper describes the behavior of Mid as seen from the environment, i.e., it describes error-free simplex point-to-point communication between two parties. In addition, since all DisCo systems are closed, i.e., contain a description of environment behavior, it needs to describe unrestricted environment behavior. Three object classes are introduced: class channel_mid is state idle, busy(data: message); initially idle; end; class sender_mid(n: integer; to: channel_mid) is assert n > 0 ∧ to ≠ null; queue: sequence message; initially queue = ; end; class receiver_mid(n: integer; from: channel_mid) is assert n > 0 ∧ from ≠ null; queue: sequence message; initially queue = ; end; assert ∀ c: channel_mid:: size(s: sender_mid | s.to = c) = size(r: receiver_mid | r.from = c) = 1;

A channel_mid object represents a reliable channel between sender and receiver objects. It has two exclusive states: either it is idle, which is the initial state, or it is busy with a message that is kept in the associated variable data. The parameter notation for data indicates that its value cannot be changed without exiting and re-entering this state. At this point it should be pointed out that channel_mid objects should, in fact, be understood as hidden internal variables in this specification, corresponding to quantified program variables in TLA. Notice also that a higher-level specification could easily be given without such objects, and Upper could then be understood as a refinement where their introduction is based on a design decision. Each sender_mid has two parameters whose values are set at initialization: n is the capacity of the associated buffer, and to is the associated channel. In addition, it has a variable

-7-

containing the buffer itself, i.e., a sequence of items of type message. Correspondingly, each receiver_mid is associated with information on its buffer size, the associated channel, and the contents of the buffer. All buffers are initialized as empty. The assertions state that each sender and receiver has a buffer and is associated with an existing channel, and that each channel is point-to-point between one sender and one receiver. No restrictions are given on their number. Figure 2 illustrates the case where only one channel exists. In terms of TLA these class declarations would introduce program variables, so that each state declaration and each variable declaration (including state parameters) would correspond to a separate array, with subscripts identifying the existing objects. Assertions and initial conditions give the initial conditions for these TLA program variables. Four actions are introduced (in Figure 2 they are given as arrows indicating the direction of message flow between participants): action enter_mid(m: message) by sm: sender_mid is when size(sm.queue) < sm.n do sm.queue := sm.queue & ; end; action send_mid(m: message) by sm: sender_mid; cm: channel_mid is when sm.to = cm ∧ size(sm.queue) > 0 ∧ cm.idle ∧ m = head(sm.queue) do → cm.busy(m); sm.queue := tail(sm.queue); end; action receive_mid(m: message) by rm: receiver_mid; cm: channel_mid is when rm.from = cm ∧ size(rm.queue) < rm.n ∧ m = cm.busy.data do rm.queue := rm.queue & ; → cm.idle; end; action exit_mid(m: message) by rm: receiver_mid is when size(rm.queue) > 0 ∧ m = head (rm.queue) do rm.queue := tail(rm.queue); end;

Action enter_mid describes an interaction where a message is accepted to a sender_mid object. Parameter m denotes an arbitrary message that is generated nondeterministically in this action. The action is enabled if the buffer capacity has not been exhausted, and its effect is to append the message to this buffer. The type message is left arbitrary, but it can be specialized to concrete message types later, as will be shown below. Whenever there are messages in a sender buffer and the associated channel is idle, action send_mid puts the next message into the channel. In order to allow easy reference to the message in later refinements of this action, it is given as an action parameter, even though its value is uniquely determined by the guard. The arrow in the action body denotes a state transition statement where the channel enters state busy, with the value of its state parameter set to the message. Correspondingly, whenever there is a message in a channel and the buffer of the associated receiver is not full, action receive_mid appends the message to this buffer and causes an associated state transition in the channel. The message is again given as an action parameter in order to allow easy reference to it later.

-8-

sender_high enter_mid

receiver_high

send_high sender_mid

send_mid

receive_high channel_mid

receive_mid

exit_mid

receiver_mid

Figure 3. Objects and actions in High, with imported components drawn in grey.

Finally, action exit_mid describes an interaction where a transmitted message is given to the environment in the receiving end. Again, the message is a parameter whose value is uniquely determined by the guard. Correspondence to TLA actions should be obvious. As explained in Section 3.2, parameters and participants correspond to existentially quantified variables, guards are predicates on the source state of a transition, and bodies correspond to predicates involving both source and target states. With these class and action declarations, the system is complete in the sense that it can be simulated. Like all DisCo systems, it describes the behavior of both the system and its environment at a selected level of abstraction. 4.2. Application Layer As a simple application of this communication system we consider a situation where one sender is sending consecutive integers to a receiver that keeps computing their sum. Module Upper is imported, and the application is added to the system by superposition. In this process the type message is specialized into an integer, as required by the application. This means that such refinements are generated of enter_mid and exit_mid where the messages are single integers. These refinements are renamed as send_high and receive_high, respectively. The old names enter_mid and exit_mid remain to denote such implicit refinements where the message type has not been specialized. New class definitions are introduced to describe the sender and receiver objects of the application. Both need knowledge about the object with which to communicate in Upper. In addition, the sender needs a variable for the integers to be generated, and the receiver needs one for the sum to be computed: class sender_high(sm: sender_mid) is assert sm ≠ nil; i: integer; initially i = 0; end; class receiver_high(rm: receiver_mid) is assert rm ≠ nil; sum: integer; initially sum = 0; end; assert size(s: sender_high) = size(r: receiver_high) = 1 ∧ ∀ s: sender_high, r: receiver_high:: s.sm.to = r.rm.from;

No new actions are introduced, but send_high and receive_high are refined as follows:

-9-

enter_low

exit_low

sender_low

channel_low send_low

receiver_low receive_low

drop

Figure 4. Objects and actions in the lower interface. refined send_high by ... sh: sender_high is -- derived of enter_mid(m) by sm where m = when ... sh.sm = sm ∧ m.value = sh.i do ... sh.i := sh.i + 1; end; refined receive_high by ... rh: receiver_high is -- derived of exit_mid(m) by rm where m = when ... rh.rm = rm do ... rh.sum := rh.sum + m.value; end;

Ellipses are a notation denoting parts taken from imported actions; double hyphens begin comments. The sender of the application level is added as a participant to send_high. The value to be communicated is restricted to be the value of i within sender_high, and variable i is also incremented. Correspondingly, the receiver of the application level is added to participate in receive_high, which is also refined to update the sum. Figure 3 indicates the structure of High, including the imported upper interface. The nonspecialized actions enter_mid and exit_mid are still available for other specializations. This means that the same channel could be used for other purposes also. Since different versions of enter_mid and exit_mid only deal with the associated specializations of the message type, there is no danger of different kinds of messages to be confused. Actions send_mid and receive_mid, on the other hand, do not distinguish between such specializations, and they transmit all messages regardless of their structures. In terms of TLA, the new class declarations introduce new program variables, the new assertions strengthen the initial condition, and action refinements strengthen the TLA actions. 4.3. Lower Interface Module Lower, which is illustrated in Figure 4, is similar to Upper, except that there is no buffering, and messages may be lost arbitrarily by a drop action. Since its actual representation in DisCo is unimportant for the purposes of this paper, it will be omitted. 4.4. Mid-Layer Module Mid demonstrates the refinement of a combination of two independent systems. Its purpose is to implement Upper in terms of Lower. The two systems are first combined, and this combination is then refined by superposition. To achieve reliable communication, separate channels of Lower are used for actual messages and for acknowledgments, and alternating bits are used to distinguish between consecutive messages. The resulting system still contains the reliable channel, but this has become virtual in the sense that it can be optimized away.

- 10 -

High sender_high

receiver_high

send_high

receive_high

enter_mid sender_mid

Mid

send_mid

receive_mid channel_mid

send_msg

Lower

exit_low

sender_low

receiver_mid

send_ack

receive_thru_low

receive_ack enter_low

exit_mid

Upper

receive_msg exit_low

receiver_low

receiver_low

receive_low

receive_low

send_low

enter_low sender_low

send_low channel_low

channel_low

drop

drop

Figure 5. Objects and actions in High_and_Mid, with projections to all imported systems.

Rather than giving the details of Mid in DisCo, we illustrate it in Figure 5 together with with which it forms the combined system High_and_Mid of Figure 1. The instantiation of Lower in Mid contains two (unreliable) channels, one for the actual messages (together with alternating bits), the other for acknowledgments (consisting of alternating bits only). The instantiation of Upper contains just one (reliable) channel. Since Upper and Lower have no common predecessor systems, all their actions are pairwise compatible. Therefore, the combined system would include all possible combined actions, including the ones where the stuttering action is taken from one component. According to the conventions of DisCo, all combinations with stuttering actions are always automatically included, but combinations of independent actions need be explicitly requested. In the following we comment briefly on all the actions of Mid shown in Figure 5. Action send_mid is a refinement that also stores the message in a special one-message buffer that is added to sender_mid. Once the message is here, action send_msg, which is a refinement of enter_low, gives it to sender_low. After this, actions send_low and receive_low may transmit it to the corresponding receiver_low, but it may also be lost in a drop action. As long as no acknowledgment is received, this procedure is repeated. Action receive_thru_low is a combination of receive_mid and exit_low. It becomes enabled once the same message is both in channel_mid and in the receiver_low, and it accepts the message to receiver_mid as defined by the functionality of receive_mid. Action receive_msg is another (non-combined) refinement of exit_low, which removes redundant messages that are obtained through the unreliable channel. The non-combined action receive_mid has been removed by strengthening its guard to be identically false. High,

- 11 -

Once a new message has been received in receive_thru_low, another refinement of enter_low, send_ack, becomes enabled for an acknowledgment, which is eventually transmitted by send_low and receive_low to the corresponding receiver_low, from where receive_ack (a refinement of exit_low) takes it to sender_mid making this ready for the next message. Notice that the reliable channel is still present in the system, even though the purpose was to implement it in terms of two unreliable channels. With suitable invariants it can be proved, however, that channel_mid has effectively turned into a ghost variable that could be deleted. Since no proof system is assumed in the language, the removal of variables is not supported by any of its constructions. Therefore, channel_mid remains there, and is still available as an abstraction in system animation.

5. Discussion DisCo is a language that draws from the tradition of programming language design. Calling it a specification language is justified by features that do not allow direct implementation, or cannot be automatically implemented in a distributed fashion. The main instances of such features are unboundedly nondeterministic action parameters, fairness conditions (which were omitted here), and non-separable guards, i.e., guards that do not directly allow distributed evaluation. In such respects DisCo is at a slightly higher level of abstraction than LOTOS [BB87], and clearly further away from programming languages than IP [FF90]. However, DisCo specifications are executable in the sense that their simulation is possible with some interactive guidance from the user. Under certain conditions they can be automatically compiled into distributed programs, which allows to use a transformational approach in system development. TLA, on the other hand, is a logic for expressing and proving properties of reactive systems. A close relationship exists between it and DisCo, in the sense that DisCo can be easily understood in its terms. Therefore, TLA is a natural vehicle for expressing properties of DisCo systems and for reasoning about them. Without an associated logic DisCo is actually insufficient for such purposes, and in this respect it is at the level of programming languages. As argued by Lamport [La90], logic is by itself sufficient for specifying reactive systems. As a language for specification and design, DisCo goes beyond the needs of pure specification. Logically this may seem like introducing implementation-oriented details that do not belong to specifications. Channel objects in the above example illustrate design decisions that would not be needed in a pure specification. We support the view, however, that formal specification should not be completely separated from design, and that a specification language should therefore contain facilities that are useful for expressing the designer's intentions and for supporting structured development of systems. The crucial questions are, of course, whether such facilities obscure the main issues by making reasoning more difficult, whether they restrict specifications unnecessarily, and whether they lead to too early commitment to certain implementation decisions. For DisCo, the first question is answered by its close relationship to TLA; the two other questions will be addressed below.

- 12 -

5.1. Structuring of State System state is partitioned in DisCo into objects of given classes. No variables can be introduced without associating them with objects. Logically such a partitioning would not be needed, and it is not present in the more general work on joint actions [BK83, BK84, BK80a, BK80b, BK90]. Its essential effect is to augment each action with an existential quantification over all objects of certain classes. Obviously this has no effect on the expressiveness of the language, but it has proved useful in supporting a style where artificial indexing and dependence on scaling factors are easily avoided. In case studies this has improved the readability and reusability of specifications. In using DisCo for design, the convention is useful that the local state of one object will not be distributed to several processes. Refinement by superposition or by other kinds of transformations can then be used to achieve a form that allows natural implementation with this assumption. Even though partitioning into objects then reflects a design decision, it does not complicate logical reasoning. In contrast to process-oriented languages like LOTOS [BB87] and IP [FF90], DisCo specifications make no distinction whether an object represents an active agent or a passive data structure. Notice that such a distinction is analogous to specifying whether a sequential program should be compiled into “active” code or interpreted in a “passive” internal representation. A notion of processes, which makes this distinction, has an effect on reasoning, and it also means a stronger commitment or bias for implementations. Some notion of this kind is, however, helpful in the visualization and animation of executable specifications, but objects are in this respect as good as processes. Within objects, DisCo combines facilities that are adopted from high-level programming languages and from hierarchical state-transition systems. Typical examples are declaration of typed variables with scopes restricted to certain states. Logically these facilities introduce invariants that could alternatively be formulated as simple theorems. Experience with programming languages shows that such facilities are desirable for expressing the intentions of the designer, and that they can improve the readability of specifications. 5.2. Actions and Modularity The body of each action is given in DisCo in terms of deterministic assignments. (A state transition statement can be understood as a syntactic variant of an assignment.) Since loops are disallowed, bodies can easily be converted into predicates on source and target states. Therefore, this difference between DisCo and TLA is only superficial. At first sight it might seem that determinism in action bodies would restrict the expressiveness of DisCo. However, because of the facility for action parameters, no generality is lost. In fact, for a given relation between old and new states, a DisCo action can always be written so that the new state is given by parameters, and the relation is expressed in the guard, in which case the body becomes trivially deterministic. From the viewpoint of pure specifications the separation of nondeterministic parameters from deterministic bodies has no significance. However, in the development of executable specifications this does make a difference. Deterministic bodies are directly executable, while nondeterministic choices may need guidance from the user. Action parameters provide convenient handles for expressing such choices and also for restricting them with superposition.

- 13 -

Compared to actions in TLA there is, however, one restriction: a single DisCo action can involve only a bounded number of individual changes in the state. This we do not regard as a severe limitation for reactive systems, where atomic state changes tend to be small. As a syntactic refinement mechanism superposition is obviously not as general as a semantically defined data refinement [Ba90, BK90]. The price of a more general mechanism would be a need for an associated proof system. The main limitation of superposition is that it can only extend the state but cannot at the same time remove old state components, which was also demonstrated by the above example. As for modularity, it was already stated in Section 3.4 that a combined specification in DisCo is, in general, stronger than the weakest specification that would imply all the component specifications. What is achieved, however, is that action guards and bodies in the combined specification can be directly constructed from those in the components. For practical design we consider this kind of simplicity to be more important than the minimality of specifications.

6. Concluding Remarks The basic notions and constructs of DisCo have been described in temporal logic of actions. Due to the simple execution model, and to the small number of basic notions, this is relatively straightforward. In fact, DisCo may be thought of as a TLA-oriented programming language, with an object-oriented structuring of state, and with syntactic support for modularity and readability. Similarly to high-level programming languages it guarantees syntactically whatever is possible, without relying on automated proof systems. Its constructions for superposition and combination of specifications are therefore less general than what would be semantically necessary. It also contains language facilities that are superfluous from a purely logical viewpoint, but which can be justified by their support to the design of reusable, executable specifications. In this paper we have omitted all liveness properties. This was not because of any fundamental differences in dealing with them. In fact, both DisCo and TLA use fairness with respect to actions. Preservation of liveness properties is, however, more difficult to guarantee in modular construction of specifications, and cannot, in general, be treated syntactically. For simplicity, we have also omitted the DisCo notion of inheritance, which allows somewhat more general combination of specifications than what was discussed here, and is suited for certain situations of bottom-up development. Acknowledgments The joint action approach was developed together with Ralph Back from Åbo Akademi. Research on its use as a basis for a practical specification language has been carried out in project DisCo at Tampere University of Technology. This ongoing project is part of the FINSOFT programme of the Technology Development Centre of Finland (TEKES), and is supported by four industrial partners. We are also indebted to Leslie Lamport for enlightening discussions on whether programming languages support or confuse the construction of specifications.

- 14 -

References [AL88] [Ba90]

[BK83]

[BK84]

[BK88a]

[BK88b] [BK90] [BB87] [CM88] [FF90]

[JK90a] [JK90b] [JKSS90]

[Ku90] [KJ89]

[KK88] [LS90] [La90] [MP83] [Pn86]

Abadi, M., Lamport, L., The existence of refinement mappings. Research Report 29, Digital Systems Research Center, 1988. To appear in Theoretical Computer Science. Back, R.J.R., Refinement calculus II: parallel and reactive programs. In Stepwise Refinement of Distributed Systems: Models, Formalisms, Correctness, LNCS 430, Springer-Verlag 1990, 6793. Back, R.J.R., Kurki-Suonio, R., Decentralization of process nets with a centralized control. Distributed Computing 3 (1989), 73-87. An earlier version in 2nd ACM SIGACT-SIGOPS Symposium on Principles of Distributed Computing, Montreal, Canada, Aug. 1983, 131-142. Back, R.J.R., Kurki-Suonio, R., A case study in constructing distributed algorithms: distributed exchange sort. In Proc. Winter School on Theoretical Computer Science, Lammi, Finland, Jan. 1984, Finnish Society of Information Processing Science, 1-33. Back, R.J.R., Kurki-Suonio, R., Serializability in distributed systems with handshaking. In Automata, Languages and Programming (Ed. T. Lepistö and A. Salomaa), LNCS 317, SpringerVerlag 1988, 52-66. Back, R.J.R., Kurki-Suonio, R., Distributed cooperation with action systems. ACM Trans. Programming Languages and Systems 10, 4 (Oct. 1988), 513-554. Back, R.J.R., Kurki-Suonio, R., Superposition and fairness in reactive system refinement. To be presented at the 5th Jerusalem Conference on Information Technology, Jerusalem, Oct. 1990. Bolognesi, T., Brinksma, E., Introduction to the ISO specification language LOTOS. Computer Networks and ISDN Systems 14, (1987), 25-59. Chandy, K.M., Misra, J., Parallel Program Design: A Foundation. Addison-Wesley, 1988. Francez, N., Forman, I.R., Interacting processes: a language for coordinated distributed programming. To be presented at the 5th Jerusalem Conference on Information Technology, Jerusalem, Oct. 1990. Järvinen, H.-M., Kurki-Suonio, R., The DisCo language. Tampere University of Technology, Software Systems Laboratory, Report 8, 1990. Järvinen, H.-M., Kurki-Suonio, R., DisCo specification language: marriage of actions and objects. Manuscript, 1990. Järvinen, H.-M., Kurki-Suonio, R., Sakkinen, M., Systä, K., Object-oriented specification of reactive systems. Proc. 12th International Conference on Software Engineering, Nice, France, March 1990, IEEE Computer Society Press, 63-71. Kurki-Suonio, R., Operational specification with joint actions: serializable databases. To appear in Distributed Computing. Kurki-Suonio, R., Järvinen, H.-M., Action system approach to the specification and design of distributed systems. In Proc. Fifth International Workshop on Software Specification and Design. ACM Software Engineering Notes 14, 3 (May 1989), 34-40. Kurki-Suonio, R., Kankaanpää, T., On the design of reactive systems. BIT 28, 3 (1988), 581604. Lam, S.S., Shankar, A.U., A relational notation for state transition systems. IEEE Trans. on Software Engineering 16, 7 (July 1990), 755-775. Lamport, L., A temporal logic of actions. Research Report 57, Digital systems Research Center, 1990. Manna, Z., Pnueli, A., How to cook a temporal proof system for your pet language. In Proc. 10th ACM Symposium on Principles of Programming Languages, Austin, Texas, Jan. 1983, 141-154. Pnueli, A., Applications of temporal logic to the specification and verification of reactive systems: a survey of current trends. In Current Trends in Concurrency (Ed. J.W. de Bakker, W.-P. de Roever and G. Rozenberg), LNCS 224, Springer-Verlag 1986, 510-584.

Suggest Documents