An Empirical Comparison of Formalisms for Modelling and Analysis of Dynamic Reconfiguration of Dependable Systems

An Empirical Comparison of Formalisms for Modelling and Analysis of Dynamic Reconfiguration of Dependable Systems

Abstract

This paper uses a case study to evaluate empirically three formalisms of different kinds for their suitability for the modelling and analysis of dynamic reconfiguration of dependable systems. The requirements on an ideal formalism for dynamic software reconfiguration are defined. The reconfiguration of an office workflow for order processing is described, and the requirements on the reconfiguration of the workflow are defined. The workflow is modelled using the Vienna Development Method (VDM), conditional partial order graphs (CPOGs), and the basic Calculus of Communicating Systems for dynamic process reconfiguration (basic ), and verification of the reconfiguration requirements is attempted using the models. The formalisms are evaluated according to their ability to model the reconfiguration of the workflow, to verify the requirements on the workflow’s reconfiguration, and to meet the requirements on an ideal formalism.

d
\newlist

formalismreqsenumerate1 \newlistreconfigreqsenumerate1 \newlistworkflow1reqsenumerate1 \newlistworkflow2reqsenumerate1 \newlistdesignlistenumerate1 \newlistvdmtodoitemize1 A. Bhattacharyya and A. Mokhov and K. Pierce] Anirban Bhattacharyya and Andrey Mokhov and Ken Pierce
School of Computing Science, Claremont Tower, Newcastle University, Newcastle upon Tyne, NE1 7RU, UK
School of Electrical and Electronic Engineering, Merz Court, Newcastle University, Newcastle upon Tyne, NE1 7RU, UK \correspondAnirban Bhattacharyya, School of Computing Science, Claremont Tower, Newcastle University, Claremont Road, Newcastle upon Tyne, NE1 7RU, UK. E-mail: A.Bhattacharyya@ncl.ac.uk \counterwithinlstlistingsection

\makecorrespond

ynamic software reconfiguration, workflow case study, reconfiguration requirements, formal methods, VDM, conditional partial order graphs, basic 

1 Introduction

The next generation of dependable systems is expected to have significant evolution requirements [CHNF10]. Moreover, it is impossible to foresee all the requirements that a system will have to meet in future when the system is being designed [MMR10]. Therefore, it is highly likely that the system will have to be redesigned (i.e. reconfigured) during its lifetime, in order to meet new requirements. Furthermore, certain classes of dependable systems, such as control systems, must be dynamically reconfigured [KMOS10], because it is unsafe or impractical or too expensive to do otherwise. The dynamic reconfiguration of a system is defined as the change at runtime of the structure of the system – consisting of its components and their communication links – or the hardware location of its software components [Bha13] or their communication links. For example, the dynamic upgrade of a software module in a telecommunications satellite during the execution of the old version of the module, the removal of a faulty controller of an aero engine during flight, re-establishing the exchange-to-mobile communication link during a conversation as the mobile crosses the boundary of communication zones, and moving an executing software object between servers to achieve load balancing. This paper focuses on dynamic software reconfiguration, because software is much more mutable than hardware.

Existing research in dynamic software reconfiguration can be grouped into three cases from the viewpoint of interference between application and reconfiguration tasks, which is embedded in time (see Figure 1). Interference is defined as the effect of the concurrent execution of a task on the execution of another task. For example, an incorrect result of a computation performed by the affected task, or a delay in the response time of the computation, or the delayed termination or replacement of the task.

Figure 1: Dynamic reconfiguration cases

Case 1 is the near-instantaneous reconfiguration of a system, in which the duration of the reconfiguration interval is negligible in comparison to the durations of application tasks. Any executing task in Configuration 1 that is not in Configuration 2 is aborted, which can leave data in a corrupted or inconsistent state. Alternatively, the reconfiguration is done at the end of the hyperperiod of Configuration 1 (i.e. the lowest common multiple of the periods of the periodic tasks in Configuration 1), which can result in a significant delay in handling the reconfiguration-triggering event. This is the traditional method of software reconfiguration, and is applicable to small, simple systems running on a uniprocessor.

Case 2 is the reconfiguration of a system in which the duration of the reconfiguration interval is significant in comparison to the durations of application tasks, and any executing task in Configuration 1 that can interfere with a reconfiguration task is either aborted or suspended until the reconfiguration is complete. This is the most common method of software reconfiguration (see [SVK97], [AWvSN01], [BD93], and [KM90]), and is applicable to some large, complex, distributed systems. If the duration of the reconfiguration is bounded and the controlled environment can wait for the entire reconfiguration to complete, then the method can be used for hard real-time systems; otherwise, the environment can become irrecoverably unstable and suffer catastrophic failure if a time-critical service is delayed beyond its deadline.

Case 3 is the reconfiguration of a system in which the duration of the reconfiguration interval is significant in comparison to the durations of application tasks, and tasks in Configuration 1 execute concurrently with reconfiguration tasks. This method avoids aborting tasks and reduces the delay on the application due to reconfiguration, but it introduces the possibility of functional and timing interference between application and reconfiguration tasks. If the interference can be controlled, then this method is the most suitable for large, complex, distributed systems, including hard real-time systems, but it is also the least researched method of software reconfiguration. Existing research in Case 3 has focused on timing interference between application and reconfiguration tasks, and on achieving schedulability guarantees (for example, see [SRLR89], [TBW92], [Ped99], [Mon04], [FW05], and [F0̆6]). There is little research on formal verification of functional correctness in the presence of functional interference between application and reconfiguration tasks (see [MT00] and [BCDW04]).

Therefore, there is a requirement for formal representations of dynamic software reconfiguration that can express functional interference between application and reconfiguration tasks, and can be analyzed to verify functional correctness. There is also a significant requirement for modelling unplanned reconfiguration, that is, reconfiguration that is not incorporated in the design of a system (see [CHNF10], [MMR10], and [KMOS10]). This paper makes a contribution towards meeting these two key requirements. Research shows that no single existing formalism is ideal for representing dynamic software reconfiguration [Wer99]. Therefore, we use three formalisms of different kinds: the Vienna Development Method (VDM, based on the state-based approach), conditional partial order graphs (CPOGs, in which graph families are used for verification of workflow and reconfiguration requirements), and the basic Calculus of Communicating Systems for dynamic process reconfiguration (basic , based on the behavioural approach), to produce representations of a case study, and evaluate how well the different representations meet the requirements. The diversity of the three formalisms is manifested in the diversity of their semantics. Thus, VDM has a denotational semantics in order to describe data structures and algorithms, which helps to refine a specification to an implementation; CPOGs have an axiomatic semantics based on equations, which helps to make reasoning about graphs simple and computationally efficient; and basic has a labelled transition system (LTS) semantics, which helps to describe process behaviour and reconfiguration simply. In order to facilitate comparison of the formalisms, we have defined an LTS semantics for CPOGs and for the VDM model; an LTS semantics for VDM is beyond the scope of this paper.

The rest of the paper is organized as follows: Section 2 defines requirements on an ideal formalism for the modelling and analysis of dynamic software reconfiguration. Section 3 describes the case study, in which a simple office workflow for order processing is dynamically reconfigured, and defines the requirements on the initial configuration, the final configuration, and on the reconfiguration of the workflow. The case study is a modification of the case study used in [Bha13] to evaluate basic . The reconfiguration of the workflow is modelled and analyzed using VDM (in Section 4), CPOGs (in Section 5), and basic (in Section 6). We have deliberately not used workflow-specific formalisms (such as [YL05], [AP07], and [HND11]) for two reasons. First, because of our lack of fluency in them; and second, because we believe the models should be produced using general purpose formalisms (if possible). VDM, CPOGs, and basic are compared and evaluated in Section 7 using the results of the modelling and analysis exercise and the requirements on an ideal formalism defined in Section 2. Related work is reviewed in Section 8.

This paper contains considerable material from the first author’s doctoral thesis [Bha13]. The core requirements on an ideal formalism F1–F11 in the following section are from the thesis, requirement F12 is new and was suggested by one of the anonymous reviewers. The case study is a modification of the thesis case study: Configuration 1 has been simplified by making it purely linear, Configuration 2 has been made more complex by adding concurrently executing tasks, and the reverse reconfiguration from Configuration 2 to Configuration 1 is now considered in the modelling. The descriptions of the syntax and semantics of basic are from the thesis, but the modelling now refers to all the designs of Configuration 1 (rather than to only Design 3) and the analysis based on weak observational bisimulation is new. The sections on VDM, CPOGs, and the comparison of formalisms are (of course) new.

2 Requirements on an Ideal Formalism for Dynamic Software Reconfiguration

No single existing formalism is ideal for the modelling and analysis of dynamic software reconfiguration [Wer99]. However, it is possible to identify core requirements that an ideal formalism must meet and to evaluate candidate formalisms against these requirements, such as those in this paper. In this section, we identify and briefly justify a set of core requirements, labelled F1–F12, and use them to evaluate the three formalisms in Section 7. We summarize our findings at the end of this section.

2.1 Requirements on Formalisms

We divide the requirements into two groups, the first relating specifically to modelling and verifying dynamic reconfiguration, and the second relating more generally to ‘usable’ formalisms.

Dynamic Reconfiguration Requirements

{formalismreqs}

It should be possible to model, and to identify instances of, software components and tasks, and their communication links. A software component can be a program or a class (as in Smalltalk or C++) or a module (as in C), a task is a process (as in UNIX), and a communication link is a channel of communication (e.g. a socket-to-socket link over TCP/IP between communicating UNIX processes). Multiple tasks can be based on the same software component in order to process different transactions concurrently, and multiple software components can be used to provide fault tolerance. The dynamic reconfiguration of a software component or of a task involves the selective reconfiguration of its instances, which is facilitated by the use of instance identifiers.

It should be possible to model the creation, deletion, and replacement of software components and tasks, and the creation and deletion of their communication links. These are the fundamental operations used to change the software structure of a system.

It should be possible to model the relocation of software components and tasks on physical nodes. Software relocation helps to implement load balancing, which is used to improve performance and reliability in cloud computing. Thus, software relocation helps to increase the dependability of cloud computing.

It should be possible to model state transfer between software components and between tasks. In dependable systems with state, state transfer helps to implement Case 2 of dynamic reconfiguration (see Figure 1) and to implement software relocation.

It should be possible to model both planned and unplanned reconfiguration. Planned reconfiguration is reconfiguration that is incorporated in the design of a system. Unplanned reconfiguration is reconfiguration that is not incorporated in the design of a system, which is relevant for legacy systems and for the evolution of systems.

It should be possible to model the functional interference between application tasks and reconfiguration tasks. This is the main modelling requirement in Case 3 of dynamic reconfiguration (see Figure 1), and is an outstanding research issue.

It should be possible to express and to verify the functional correctness requirements of application tasks and reconfiguration tasks. This is the main verification requirement of dynamic reconfiguration, and is an outstanding research issue in Case 3.

General Requirements

{formalismreqs}

[start=8]

It should be possible to model the concurrent execution of tasks. Concurrency can cause functional interference between tasks, and thereby affect the functional correctness of a task, and it is a feature of many dependable systems. Therefore, it should be modelled.

It should be possible to model state transitions of software components and tasks. State affects the functionality of a task, and thereby affects the functional correctness of the task, and it is a feature of most dependable systems. Therefore, it should be modelled.

The formalism should be as terse as possible. Terseness supports abstraction, which is essential in removing unnecessary detail from a model, and thereby renders the model easier to understand. Thus, terseness facilitates the use of a model.

The formalism should be supported by tools. Otherwise, the formalism will not be used by software engineers.

The formalism should be easy to learn and to use. Otherwise, the rate of adoption of the formalism by users will be low.

2.2 Summary of Results

The evaluations of the three formalisms in Section 7 show that none of them is ideal, since none of them meets all the requirements on an ideal formalism for dynamic software reconfiguration defined above. However, the formalisms meet the requirements collectively, and (therefore) are complementary (albeit with extensions).

The main strength of basic is in modelling. It can model: abstractly and tersely the composition and concurrent execution of application and reconfiguration tasks using concurrent processes, their functional interference using interleaved transitions, their planned and unplanned reconfiguration using fraction processes, cyclic processes using recursion, and reconfiguration of fraction processes using other fractions. Its main weaknesses are: inability to control non-deterministic transitions, inability to reconfigure selectively specific process instances, computational complexity of process matching based on bisimulation, computational complexity and restrictiveness of process congruence, and lack of tools.

In contrast, the main strength of CPOGs is in verification. A Boolean SAT solver can compare a model and its requirement in canonical form, and efficient model checking is supported by predicates on actions and on action dependencies and the acyclic topology of CPOGs. Correctness of a reconfiguration between configurations can be proved using consistent histories of actions of the two configurations and by restricting interference through forbidden actions. Functional interference between tasks can be modelled using either interleaved actions or simultaneous actions. Its main weaknesses are: inability to model composition and structure of software components and tasks, low level of abstraction for modelling, inability to model cyclic processes, and lack of available tools.

In contrast to both basic and CPOGs, the main strength of VDM is in formal software development. It can model workflows, software components, and tasks as data types, which facilitates their refinement to an implementation, its tools for development, simulation, and testing are mature and available, and it is easy to use by system designers. The main weaknesses of VDM-SL are: lack of constructs for modelling concurrency and interference, and lack of formal verification tools.

3 Case Study: Dynamic Reconfiguration of an Office Workflow for Order Processing

The case study described in this section involves the dynamic reconfiguration of a simple office workflow for order processing, which is a simplified version of real workflows commonly found in large and medium-sized organisations [EKR95]. Preliminary versions of the case study are in [MADB12] and [Bha13]. The case study was chosen for three reasons. First, workflows are ubiquitous, which suggests that our workflow reconfiguration will be of interest to a large community. Second, the case study is based on published work by other researchers, see [EKR95]. Third, it is both simple to understand and complex enough to exercise all three formalisms.

The workflow consists of a network of several communicating tasks, and the configuration of the workflow is the structure of the network. The workflow does not contain any loop, because loops tend to reduce the scope of reconfiguration considerably. A loop can have an invariant that is not an invariant of the reconfiguration, and thereby can prevent reconfiguration of tasks constituting the loop during an execution of the loop. The workflow contains the following tasks:

  • Order Receipt: an order for a product is received from an existing customer. The order identifier includes the customer identifier and the product identifier. An evaluation of the order is initiated to determine whether or not the order is viable.

  • Evaluation: the product identity is used to check the availability of the product; the customer identity is used to check the credit of the customer. If either check fails, the output is negative and the order is rejected; otherwise, the output is positive and the order is accepted.

  • Rejection: if the order is rejected, a notification of rejection is sent to the customer and the workflow terminates.

  • If the order is accepted, the following tasks are performed:

    • Shipping: the product is shipped to the customer.

    • Billing: the customer is billed for the cost of the product ordered plus shipping costs.

    • Archiving: the order is archived for future reference.

    • Confirmation: a notification of successful completion of the order is sent to the customer.

3.1 Configurations of the Workflow

There are two configurations of the workflow: Configuration 1 and Configuration 2. Initially the workflow executes Configuration 1. Subsequently, the workflow must be reconfigured through a process to Configuration 2. Requirements on the two configurations are shown in Figures 3.1 and 3.1 and are explained below. We then identify requirements on the reconfiguration from Configuration 1 to Configuration 2, and identify potential designs for this system.

\captionof figureFlow chart of the requirements on Configuration 1 \captionof figureFlow chart of the requirements on Configuration 2

The initial configuration of the workflow is Configuration 1 and must meet the following requirements for each order (see Figure 3.1):

{workflow1reqs}

Order Receipt must be performed first. That is, it must begin before any other task.

Evaluation: in evaluating the order, the product identity is used to perform an inventory check on the stock of the product, and the customer identity is used to perform a credit check on the customer. If either check fails, the output of Evaluation is negative; otherwise, the output is positive.

Evaluation must be performed second.

If the output of Evaluation is negative, Rejection must be the third and final task of the workflow.

If the output of Evaluation is positive, the following conditions must be satisfied:

  1. Shipping must be performed after Evaluation.

  2. Billing must be performed after Shipping.

  3. Archiving must be performed after Billing.

  4. Confirmation must be performed after Archiving and must be the final task to be performed.

  5. The customer must not receive more than one shipment of an order (safety requirement).

Each task must be performed at most once.

The order must be either rejected or satisfied (liveness requirement).

After some time, the management of the organization using the workflow decides to change it in order to increase sales and provide a faster service. The new configuration of the workflow is Configuration 2 and must meet the following requirements for each order (see Figure 3.1):

{workflow2reqs}

Order Receipt must be performed first.

Evaluation: in evaluating the order, the product identity is used to perform an inventory check on the stock of the product. If the inventory check fails, an external inventory check is made on the suppliers of the product. The customer identity is used to perform a credit check on the customer. If either the inventory check or the supplier check is positive, and the credit check is positive, the order is accepted; otherwise, the order is rejected.

Evaluation must be performed second.

If the output of Evaluation is negative, Rejection must be the third and final task of the workflow.

If the output of Evaluation is positive, the following conditions must be satisfied:

  1. Billing and Shipping must be performed after Evaluation.

  2. Billing and Shipping must be performed concurrently.

  3. Archiving must be performed after Billing and Shipping and must be the final task to be performed.

  4. The customer must not receive more than one shipment of an order (safety requirement).

Each task must be performed at most once.

The order must be either rejected or satisfied (liveness requirement).

3.2 Requirements on Reconfiguration of the Workflow

In order to achieve a smooth transition from Configuration 1 to Configuration 2 of the workflow, the process of reconfiguration must meet the following requirements:

{reconfigreqs}

Reconfiguration of a workflow should not necessarily result in the rejection of an order.

In some systems, executing tasks of Configuration 1 are aborted during its reconfiguration to Configuration 2 (see Case 2 in Figure 1). The purpose of this requirement is to avoid the occurrence of Case 2 and ensure the occurrence of Case 3.

Any order being processed that was received before the start of the reconfiguration must satisfy all the requirements on Configuration 2 (if possible); otherwise, all the requirements on Configuration 1 must be satisfied.

Any order received after the start of the reconfiguration must satisfy all the requirements on Configuration 2.

3.3 Designs of the Workflow and of its Reconfiguration

The two configurations of the workflow described above are stated as requirements because there are a number of ways in which an implementation, and therefore a model, can realise the workflow. We identify four such possible designs:

{designlist}

There is at most one workflow, and the workflow handles a single order at a time.

The workflow is sequential: after an order is received, the thread performs a sequence of tasks, with two choices at Evaluation. After the order has been processed, the thread is ready to receive a new order. This design corresponds to a cyclic executive.

There is at most one workflow, and the workflow can handle multiple orders at a time.

The workflow is mainly concurrent: after an order is received, the thread forks internally into concurrent threads, such that different threads perform the different tasks of the workflow – although the requirements on the configurations severely restrict the degree of internal concurrency of the workflow – and each thread performs the same task for different orders.

There can be multiple workflows, and each workflow handles a single order.

After an order is received, the thread forks into two threads: one thread processes the order sequentially – as in Design 1 – but terminates after the order has been processed; the other thread waits to receive a new order.

There can be multiple workflows, and each workflow can handle multiple orders at a time.

This design is a complex version of Design 2 with multiple workflows.

In the next three sections, we model the case study workflow and its reconfiguration using three formalisms of different kinds: VDM, CPOGs, and basic . For each formalism, we use its ’idiom’ to identify which of the above four designs of the configurations is the most suitable for the formalism, which (in turn) affects how the reconfiguration of the workflow is performed. Thus, we identify which of the three cases of reconfiguration outlined in Section 1 (instantaneous, sequential, or concurrent) is the most suitable for the ’idiom’ of the formalism.

4 Vdm

The Vienna Development Method (VDM) is a state-based formal method that was originally designed in the 1970s to give semantics to programming languages [Jon03]. Since then it has been used widely both in academia and industry to define specifications of software systems. It is well-suited to formalise requirements and natural language specifications and to find defects. For example, the FeliCa contactless card technology, which is widely used in Japan, was developed using VDM [FLS08]. A specification was constructed in VDM that revealed a large number of defects (278) in the existing natural-language requirements and specifications. The VDM specification was used to generate test cases and as a reference when writing the C code that was eventually deployed to millions of devices.

The VDM Specification Language (VDM-SL) was standardised as ISO/IEC 13817-1 in 1996 [ISO96]. Developments beginning in the 1990s extended the language to cover object-orientation (VDM [FLM05]) and later to include abstractions for modelling real-time embedded software (VDM-RT [VLH06]). All three dialects are supported by two robust tools, the commercial VDMTools [Lar01] and the open-source Overture1 tool [LBF10]. These tools offer type checking for VDM models, a number of analysis tools such as combinatorial testing [LLB10], and interpretation of an executable subset of VDM that allows models to be simulated. By connecting a graphical interface to an executable model, it is also possible to animate specifications [FLS08], thereby allowing non-specialists to gain an understanding of the system described by the specification through interaction and interrogation of the model.

The models of our case study were developed in VDM-SL (using the Overture tool) and the remainder of this section uses that dialect. As part of the standardisation process, a full denotational semantics has been defined for VDM-SL [LP95], as well as a proof theory and a comprehensive set of proof rules [BFL94]. Proofs in VDM typically verify internal consistency or are proofs of refinement [Jon90].

We proceed as follows: the VDM formalism is described in more detail in Section 4.1, the modelling of the case study is described in Section 4.2, the analysis of the model is described in Section 4.3, an LTS semantics of the model is defined in Section 4.4, and possible extensions to the model are described in Section 4.5. An evaluation of the model and formalism for describing reconfiguration is given in Section 7.

4.1 Formalism

Specifications in VDM-SL are divided into modules, where each module contains a set of definitions. Modules can export definitions to, and import definitions from, other modules in the model. The definitions in a module are divided into distinct sections and preceded by a keyword. Definitions can include types, values, functions, state, and operations. Listing 1 shows an empty VDM-SL module specification, divided into sections. We give an overview of the definitions later in this section, and further detail is introduced as required during explanation of the model.

module MyModule
exports 
imports 
definitions
types
functions
state  of
end
operations
values
end MyModule
Listing 1: Blank VDM-SL module definition

A key part of the VDM-SL language is the powerful type system. VDM-SL contains a number of built-in scalar types including booleans, numeric types, and characters. Non-scalar types include sets, sequences, and maps. Custom types can be defined in the types section, based on built-in types and including type unions and record types with named fields. Custom types can be restricted by invariants and violations of these invariants can be flagged during interpretation of the models.

Functions are pure and have no side effect. They can be defined implicitly (by pre- and post-conditions) or explicitly. Explicit functions can also be protected with a pre-condition. Only explicit functions can be interpreted by the tools. State allows one or more global variables to be defined for the module. An invariant can be defined over the state to restrict its values. Again, invariant violations can be flagged during interpretation by the tool. Operations are functions that are additionally able to read and write state variables. Therefore, operations can have side effects. Like functions, operations can be defined implicitly or explicitly. Values define constants that can be used in functions and operations.

4.2 Modelling

In creating a specification that meets the workflow requirements given in Section 3, two approaches are possible in the VDM ‘idiom’. The first is to build a data model that captures an order and its status, with operations and pre-conditions ensuring that only valid transitions between statuses are possible (e.g. order receipt to inventory check). Such a model could also include details of customers and suppliers. The second approach is to model the entire workflow as sequences of actions and build an ‘interpreter’ within the model to execute and reconfigure the workflow.

These approaches are not orthogonal; a data model would complement a workflow model. The resulting specification would to be closer to any code to be written in implementing the order system than either approach separately. In this paper we follow the second approach as this more closely matches the style of the requirements on workflows described in Section 3. We return to the data model approach in Section 4.5, giving an example of how the model could be extended to incorporate a data model for orders.

As a general purpose modelling language, VDM-SL does not have built-in notions of concurrency or threading, nor does it have dedicated abstractions for modelling processes. Following a standard VDM-SL paradigm to keep the model small, a single workflow was modelled, with concurrency of the parallel actions modelled as non-deterministic interleaving; this is Design 1 from Section 3. Reconfiguration is achieved by an operation that swaps a workflow during execution of the interpeter; this is reconfiguration Case 1 as described in Section 1. In Section 4.5 we describe extensions of the model that would facilitate exploration of Designs 2–4, including features of the other VDM dialects (VDM and VDM-RT).

Analysis of the model is done through testing. This is a common way of using VDM in industry [ALR98, FLS08], using the formal model to record and test assumptions, then using the resulting model as a specification when writing code. Tests are defined for manually checking all valid configurations, and for testing valid and invalid reconfigurations. Section 4.5 explains extensions that could facilitate greater automation in testing workflows.

The following subsections explain the modelling process in detail. The model is split into three modules: Configurations, containing workflow definitions; Interpreter, containing operations to execute and reconfigure workflows; and Test, which defines test cases for the model.

The contents of the modules are described as follows: first, a set of types is defined that can capture the two workflows in the Configurations module. Next, an interpreter is defined in the Interpreter module that can ‘execute’ a workflow2 and be reconfigured during execution. In order to test all possible paths through a configuration, a method for setting the outcome of external choices (inventory check, credit check, and supplier check) is included. The reconfiguration operation is presented in two forms: the first allows reconfiguration to any arbitrary configuration; the second extends this with a pre-condition to permit only safe reconfiguration. Finally, the Test module is described that includes operations to test all possible paths through the two configurations (in Section 4.3).

Workflows and Traces

The Configurations module defines types that are used to represent the workflows from Figures 3.1 and 3.1 and are used by the interpreter (shown later). The module also defines two constants that instantiate these workflows, a type to represent a trace of a workflow execution, and some useful auxiliary functions. The types, functions, and values of this module are made available to both the other modules using the exports all declaration:

module Configurations
exports all
definitions
end Configurations
Listing 2: Configurations module definition

The types for capturing workflows and their traces are built around a core type called Action, which enumerates all possible actions in a workflow (and therefore also in a trace). Any action must be exactly one of the nine listed values. The type union is defined using the pipe (|) operator, and the individual values are quote types (basic values that can only be compared for equality):

types
 actions in a workflow
Action = <OrderReceipt> | <InventoryCheck> | <Reject>
       | <CreditCheck> | <SupplierCheck> | <Shipping>
       | <Billing> | <Archiving> | <Confirmation>;
Listing 3: Action type definition

Based on this type, we define a trace as a sequence of events recording either the occurrence of an action, or a special <TERMINATE> event indicating successful completion of a workflow. The invariant on Trace states that if a termination event occurs, it must occur at the end (i.e. if it appears in a trace of elements and is not the only element, then it does not appear in the first elements):

types
 record of an action or termination
Event = Action | <TERMINATE>;
 trace of events
Trace = seq of Event
inv t == (<TERMINATE> in set elems t and len t > 1) =>
  <TERMINATE> not in set elems t(1, …, len t - 1);
Listing 4: Event and Trace type definitions

To define a workflow type, it is necessary to allow an order for actions to be specified; this could be done with a sequence. However, in this study a recursive definition is used based around a type called Workflow:

types
 workflow with invariant
Workflow = Element
inv w == forall tr in set tracesof(w) & card elems tr = len tr;
 elements that make up a workflow
Element =  [Simple | Branch | Par];
Listing 5: Workflow type definition

This definition states that an Element can be one of three other types (expanded below): Simple represents a single transition such as order receipt to inventory check; Branch represents an OK? choice, such as the credit check; and Par represents parallel composition. The square brackets make the type optional, meaning that it can take a fourth special value (nil) that represents termination (the black circles in Figures 3.1 and 3.1).

The Element type can be used ‘as-is’ to represent workflow configurations, but it is not restricted in any way. For example, it can contain repeated actions (i.e. billing or shipping twice). Therefore, we introduce a Workflow type3 with an invariant that prevents duplicates (by checking that for all possible traces of the workflow, the cardinality of the set of events in the trace is the same as the length of the trace).

The three types of element are defined as follows:

types
 a simple element
Simple :: a : Action
          w : Workflow;
 a conditional element
Branch ::  a : Action
           t : Workflow
           f : Workflow;
 parallel elements
Par :: b1 : Action
       b2 : Action
        w : Workflow;
Listing 6: Simple, Branch, and Par type definitions

The above three definitions are record types, that is, compound types with named elements. Each type contains one or more actions to be executed, and one or more elements that follow this action. Therefore, the definitions are recursive, and the recursion is terminated by a nil value at each leaf. A simple workflow (called T) that rejects all orders could be defined as:

values
T = mk_Simple(<OrderReceipt>, mk_Simple(<Reject>, nil))
Listing 7: Example workflow definition

Notice that the mk_ keyword is a constructor used to instantiate values of record types. They are essentially (automatically defined) functions that construct a record with the parameters being assigned, in order, to named elements.

The Configurations module also defines two auxiliary functions that are useful for invariants and pre-conditions seen later. The first (prefixof) determines if one trace is a prefix of another and the second (tracesof) recursively computes all traces of an element:

functions
 true if a is a prefix of b, false otherwise
prefixof: Trace * Trace -> bool
prefixof(a, b) == 
 compute all traces of an element
tracesof: Element -> set of Trace
tracesof(el) == 
Listing 8: Headers of the auxiliary functions prefixof and tracesof

Finally, the module defines values (constants) that describe the two configurations from Section 3, called Configuration1 and Configuration2 respectively:

values
 first configuration                          second configuration
Configuration1: Workflow =                     Configuration2: Workflow =
  mk_Simple(<OrderReceipt>,                      mk_Simple(<OrderReceipt>,
    mk_Branch(<InventoryCheck>,                    mk_Branch(<InventoryCheck>,
      mk_Branch(<CreditCheck>,                       mk_Branch(<CreditCheck>,
        mk_Simple(<Shipping>,                          mk_Par(<Billing>, <Shipping>,
          mk_Simple(<Billing>,                           mk_Simple(<Archiving>, nil)),
            mk_Simple(<Archiving>,                     mk_Simple(<Reject>, nil)
              mk_Simple(<Confirmation>, nil)         ),
            )                                        mk_Branch(<SupplierCheck>,
          )                                            mk_Branch(<CreditCheck>,
        ),                                               mk_Par(<Billing>, <Shipping>,
        mk_Simple(<Reject>, nil)                           mk_Simple(<Archiving>, nil)),
      ),                                                 mk_Simple(<Reject>, nil)
      mk_Simple(<Reject>, nil)                         ),
    )                                                  mk_Simple(<Reject>, nil)
  );                                                 )
                                                   )
                                                 );
Listing 9: Definitions of Configuration1 and Configuration2

Interpreter

The Interpreter module allows a workflow to be executed. The module exports its definitions so that they can be accessed by the Test module, and it imports required type definitions from the Configurations module, including the definitions of actions, the prefixof and tracesof auxiliary functions, and the values of Configuration1 and Configuration2.

module Interpreter
exports all
imports from Configurations types Workflow, Trace, 
Listing 10: Interpreter module definition

In the VDM idiom, models typically have persistent state, and operations that alter this state. The state of the Interpreter module records the trace of the execution so far (trace) and the remaining workflow to be executed (workflow). A state is similar to a record type and is defined in a similar manner (the state definition acts as its own section type):

 interpreter state
state S of
     trace : Trace
  workflow : Workflow
init s == s = mk_S([], nil)
end;
Listing 11: Interpreter state definition

The above state definition contains an init clause that gives initial values to both components of the state (they are both ‘empty’). An invariant can also be defined with an inv clause. The module provides operations to set (and reset) the state of the interpreter, to step through execution of a workflow or execute it in a single step, and to access the current value of the trace. Operations for reconfiguration are also included and are described below (see Reconfiguration).

An operation called Init is used to prime (set and reset) the interpreter with a workflow passed as a parameter and an empty trace:

operations
Init: Workflow ==> ()
Init(w) == (
    trace := [];
    workflow := w
);
Listing 12: Init operation definition

The basic operation of the interpreter is to move an action from the head of workflow and append it to the end of the trace. Once the workflow is empty, the <TERMINATE> element is added to the trace and the execution ends. Since there is no data model underlying the workflow, no additional work is done when moving an action from the workflow to the trace. However, an extension is considered towards this in Section 4.5.

The absence of a data model also means that the external choices in the workflow (the inventory check, credit check, and supplier check) must be made in some other manner. The main analysis method for this model is testing (described in Section 4.3). Therefore, it is desirable to be able to control these external choices to ensure test coverage. In order to do this, a Choices type is introduced, which is a mapping from (choice) actions to Boolean. The invariant ensures that the domain of the map is exactly the set of actions that represent external choices:

types
 collapse probabilities
Choices = map Action to bool
inv c == dom c = {<InventoryCheck>, <CreditCheck>, <SupplierCheck>}
Listing 13: Choices type definition

For example, a run of the workflow where there is sufficient inventory and sufficient credit can be achieved using the Choices given below. Notice that in this case a supplier check will not be needed because there is sufficient stock:

values
 all branches true
NoProblems = {<InventoryCheck> |-> true,
               <SupplierCheck> |-> true,
                 <CreditCheck> |-> true};
Listing 14: NoProblems value definition

The Interpreter module defines two operations that execute a workflow, Step and Execute, with the following signatures:

operations
 perform a single step of the interpreter
Step: Choices ==> Event
 execute workflow in one go
Execute: Choices ==> ()
Listing 15: Step and Execute operation headers

The Step operation performs a single step of execution, updating the trace and moving to the next step of the workflow. This operation selects the outcome of Branch elements based on the Choices passed as a parameter, and the order of execution of actions in Par elements are selected randomly leading to interleaving of the actions. The Step operation returns the last event that occurred, which is used for reconfiguration (described below). The Execute operation uses Step operation to execute a workflow and produce a full trace. The let expression is used to ignore the value returned by Step, since it is not needed for a simple execution run:

operations
 execute workflow in one go
Execute: Choices ==> ()
Execute(c) == (
    while workflow <> nil do
        let - = Step(c) in skip;
    trace := trace ^ [<TERMINATE>]
);
Listing 16: Execute operation definition

Reconfiguration

Reconfiguration is enabled by an operation in the Interpreter module. This operation replaces the current workflow in the state by another workflow during execution. We consider the case of a single thread of execution moving from some point in Configuration1 to an appropriate point in Configuration2, with extensions discussed later.

The following operation is defined in the Interpreter module that replaces the workflow in the state by the workflow passed as a parameter to the operation. The point at which this operation is called, and the workflow passed to the operation, are left to the caller. In this way, the model is able to capture unplanned reconfiguration.

operations
 reconfigure, replacing current workflow
Reconfigure: Workflow ==> ()
Reconfigure(w) ==
        workflow := w;
Listing 17: Unprotected Reconfigure operation definition

In this unprotected form, the calling thread is able to make arbitrary changes to the workflow, resulting in traces that do not meet the requirements described earlier. For example, double billing a customer by reconfiguring to a workflow with a <Billing> element after billing had already occurred. This is avoided by adding an invariant to the state that disallows configurations that could generate illegal traces. Additionally, a pre-condition is added to the Reconfigure operation to protect the invariant, ensuring that the operation only processes valid reconfigurations. Ideally, invariants should be protected by pre-conditions on operations in this fashion, so that invariants form a ‘last line of defence’.

It is a requirement that traces produced by the interpreter must be traces of Configuration 1 or Configuration 2. Therefore, the invariant states that, given the current trace (which can be empty), the remaining workflow can only produce traces valid under Configuration 1 or Configuration 2. Similarly, the pre-condition checks that the new workflow can only produce valid traces of Configuration 2 (since we currently only consider reconfigurations from Configuration 1 to Configuration 2). With the pre-condition added, the Reconfigure operation is defined as follows:

 reconfigure, replacing current workflow
Reconfigure: Workflow ==> ()
Reconfigure(w) ==
        workflow := w
pre w <> nil and
    branch_check(trace, workflow, w) and
    forall t in set {trace ^ ftr | ftr in set tracesof(w)} &
          (exists x in set tracesof(Configuration2) & t = x);
Listing 18: Reconfigure operation with pre-condition

The pre-condition requires first that the new workflow is not empty. Second, the branch_check auxiliary function (described below) is used to check that the reconfiguration takes account of the outcome of branching actions. Finally, the tracesof auxiliary function is used to check that for all traces in the set of traces produced by appending the possible traces of the new configuration to the current trace, that the resulting trace is a valid trace of Configuration 2. The & denotes ‘such that’ in these quantifications. Therefore, all traces that could occur after reconfiguration are valid under Configuration 2.

The branch_check auxiliary function is defined as follows:

branch_check: Trace * Workflow * Workflow -> bool
branch_check(tr, w, w’) ==
  (last(tr) = <InventoryCheck> and first(w) = {<Reject>}
    => {<SupplierCheck>} subset first(w’)) and
  (last(tr) = <InventoryCheck> and first(w) = {<CreditCheck>}
    => {<CreditCheck>} subset first(w’)) and
  (last(tr) = <CreditCheck> and first(w) = {<Reject>}
    => {<Reject>} subset first(w’)) and
  (last(tr) = <CreditCheck> and first(w) = {<Shipping>}
    => first(w’) subset {<Billing>, <Shipping>});
Listing 19: branch_check auxiliary function

The above check is necessary in order to take account of the outcome of a branching action. For example, if the inventory check passes, then the external supplier check should not be performed. Since actions are not parameterised in this model, the only way to tell which branch (true or false) was taken by the action is to examine the first element of the remaining workflow, and to check that the first element of the new workflow is a valid replacement. The branch_check function explicitly encodes this checking for the inventory check and credit check. A more general solution is to pass parameters to workflow actions (e.g. to indicate the outcome of an evaluation action) and to pass a verification condition parameter to Reconfigure. Extensions are discussed in Section 4.5.

An invariant is added to the state that is defined similarly to the pre-condition of Reconfigure. However, the invariant requires that the traces must belong to either Configuration1 or Configuration2, and prefixof is used rather than equality since the trace is incomplete in the intermediate state.

 interpreter state
state S of
     trace : Trace
  workflow : Workflow
init s == s = mk_S([], nil)
inv mk_S(trace, workflow) ==
  workflow <> nil =>
    (forall t in set {trace ^ tr | tr in set tracesof(workflow)} &
      (exists x in set tracesof(Configuration1) union tracesof(Configuration2) &
        prefixof(t, x)))
end;
Listing 20: Interpreter state definition with invariant

4.3 Analysis

The Test module defines operations that test both configurations with the five combinations of external choices. These operations call the operations of the Interpreter module (using the back tick operator: ) and output the trace, which can then be printed to the console in Overture. For example, the operation that tests Configuration1 with the NoProblems choices is defined below. This operation initialises the interpreter with Configuration1, executes the interpreter and returns the completed trace:

module Test
operations
 Test Configuration 1 / NoProblems
Config1NoProblems: () ==> Trace
Config1NoProblems() == (
        InterpreterInit(Configuration1);
        InterpreterExecute(NoProblems);
        return InterpreterGetTrace()
);
Listing 21: Config1NoProblems operation definition

When printed to the console, the output of the Config1NoProblems operation shows the following trace:

TestConfig1NoProblems() =
  [<OrderReceipt>, <InventoryCheck>, <CreditCheck>,
   <Shipping>, <Billing>, <Archiving>, <Confirmation>,
   <TERMINATE>]
Listing 22: Output of Config1NoProblems definition

Tests are also defined for the other combinations of choices, and named accordingly. For example, the test where the Choices map yields false for the credit check is called Config1NoCredit (see Appendix A).

In order to test the reconfiguration operation, and demonstrate the outcome of a valid and invalid reconfiguration request, the test module defines an operation called TestReconfig, that takes the Choices required for execution (c), an action (rp) and a workflow (w) as parameters:

operations
TestReconfig: Choices * Action * Workflow ==> ()
TestReconfig(c, rp, w) == 
Listing 23: TestReconfig operation signature

The operation initialises the interpreter, then steps through the execution until the action rp is seen, then attempts to reconfigure the interpreter to the workflow w. If the reconfiguration is valid under the requirements, the final trace will be printed. Otherwise, a message is printed stating that the reconfiguration is invalid (and that the pre-condition would fail if execution continued). In the model as presented, the w passed to the operation is constructed manually as a suffix of Configuration2. This is a weakness of the model. Discussion of automating such testing appears in Section 4.5 below.

Using TestReconfig, two operations are defined that demonstrate a valid and invalid reconfiguration respectively. The first, TestReconfigSuccess, reconfigures from Configuration1 to Configuration2 after the inventory check (where there is no inventory in stock, so a supplier check is performed). This is a valid reconfiguration, and the console output is as follows:

[<OrderReceipt>, <InventoryCheck>]
Reconfiguring Configuration1 <@to@> Configuration2
[<OrderReceipt>, <InventoryCheck>, <SupplierCheck>,
 <CreditCheck>, <Billing>, <Shipping>, <Archiving>,
 <TERMINATE>]
Listing 24: Output of TestReconfigSuccess operation

The second operation, TestReconfigFail, attempts to reconfigure from Configuration1 to the parallel composition of shipping and billing in Configuration2 after shipping has already occurred. This is an invalid reconfiguration, since shipping will occur twice. The output on the console is as below:

[<OrderReceipt>, <InventoryCheck>, <CreditCheck>,
 <Shipping>]
Reconfiguring Configuration1 <@to@> Configuration2
These potential <@traces@> are <@not@> valid under Configuration2:
* [<OrderReceipt>, <InventoryCheck>, <CreditCheck>,
   <Shipping>, <Billing>, <Shipping>, <Archiving>]
* [<OrderReceipt>, <InventoryCheck>, <CreditCheck>,
   <Shipping>, <Shipping>, <Billing>, <Archiving>]
Listing 25: Output of TestReconfigFail operation

Notice that the Overture tool terminates with an error due to pre-condition failure:

Reconfiguration could generate invalid <@traces@>; <@pre@>-
condition will fail.
<@\color{red}Error 4071: Precondition failure: pre\_Reconfigure in@>
<@\color{red}’Interpreter@>
Listing 26: Pre-condition failure reported by the Overture tool

4.4 LTS Semantics

As part of the ISO standard, a full denotational semantics has been defined for VDM-SL [LP95, ISO96]. Structured operational semantics (SOS) have also been defined for some of the new language features introduced in newer dialects [LCL13]. However, in order to facilitate comparison with the other two formalisms, we develop a labelled transition system (LTS) semantics for the VDM model. Such an LTS would be unwieldy for the whole semantics defined in the ISO standard, so we restrict this LTS to this model of the case study. This means that if the model were to be changed, the LTS would have to be updated. In the general case, this is unwieldy and is not typically part of a VDM development, but it is instructive in this instance.

The LTS rules are given in Table 1. The rules are relations that define transitions between tuples of type , corresponding to the state (S) of the interpreter. The labels on the transitions correspond to the actions that the workflow performs (which are appended to the trace), or for unobservable steps (that do not append items to the trace). The LTS rules are defined using the choices and pre_Reconfigure functions defined in the VDM model in order to render the rule definitions concise and human-readable.

The Init rule states that the interpreter can be given a workflow when it does not currently have one and its trace is empty. This is the initial state of S as defined above: init s == s = mk_S([], nil). The Terminate rule allows the interpreter to terminate when it has no workflow left. The Reset rule allows a terminated interpreter to be reset with another workflow.

The Simple, Branch-T, Branch-F, Par-1 and Par-2 rules encode the logic of the Step operation introduced above (though not given in full). They correspond to the three elements that form the Workflow type. The action of a Simple element can always happen. The action performed by a Branch action depends on the choices parameter that is passed to the Execute operation. For the Par element, the two rules have no hypotheses and therefore it is non-deterministic choice of which action is performed first. These rules make it clear that this model has an interleaving semantics. The Reconfigure rule states that the workflow that is still to be executed can be replaced in one atomic step, assuming that the new workflow will not violate the pre-condition of the Reconfigure operation.

We give three examples of application of the LTS rules in Figure 2. The first (top) demonstrates a complete run of the workflow and is equivalent to the Config1NoProblems test. The second (middle) demonstrates a run in which the credit check yields false and is equivalent to the Config1NoCredit test. The third (bottom) demonstrates a reconfiguration from Configuration 1 to Configuration 2 after the inventory check and is equivalent to the TestReconfigSuccess test. Model checking of the reconfiguration is facilitated by the trace of actions and the LTS rule applications, and is shown in Appendix B.

Table 1: Labelled transition system semantics of the basic VDM model
nil
by Init
by Simple
by Branch-T
by Branch-T
by Simple
by Simple
by Simple
by Simple
by Terminate
nil by Reset

nil
by Init
by Simple
by Branch-T
by Branch-F
by Simple
by Terminate
nil by Reset

nil
by Init
by Simple
by Branch-F
by Reconfigure
by Branch-T
by Branch-T
by Par-1
by Simple
by Simple
by Terminate
nil by Reset
Figure 2: Examples of LTS rule applications for the VDM model

4.5 Extensions

The model describes an interpreter with a single thread. Therefore, interference is not considered beyond the non-deterministic execution of parallel compositions. To allow multiple threads of control, the state of the interpreter must be extended to allow a set of workflows (and their associated traces) to be defined. The reconfiguration operation must then be extended to reconfigure each thread in turn. This would allow the model to exhibit concurrent application and reconfiguration actions (Case 3 in Figure 1). Actions are atomic in the current model, so there is no way to abort actions. To extend the model to allow this, a notion of beginning and completing actions would be required. This could be achieved either by having ‘begin’ and ‘end’ forms of each action, as used in [CJ07] to investigate fine-grained concurrency in programming languages, or by defining a ‘current action’ in the state, which is placed there and removed at a later state. If reconfiguration occurs between the beginning and end of an action, or if there is a current action present in the state, then an abort occurs.

As mentioned at the beginning of this section, there are two further dialects of VDM. These use VDM-SL as their core specification language, but extend it with additional features. VDM adds object-orientation and concurrency through threads. VDM-RT extends VDM to add features for modelling real-time embedded systems. These are a global ‘wall clock’ that is advanced as expressions are evaluated (to predict real-world execution time), and models of compute nodes connected by buses on which objects can be deployed. The (simulated) time taken to evaluate language expressions depends on the speed of the compute nodes and buses, and can be overridden to tune model where measurements of the actual speed of the target system can be made, in order to make better predictions of behaviour of the final code.

These two dialects are often used as part of a development process [LFW09] that begins with a sequential model of the system, which can be constructed in VDM-SL or in VDM. Conversion from VDM-SL to VDM is straightforward as the core language is the same, the main changes are to turn modules into classes and to turn the state definition into instance variables. The sequential model is then extended to allow concurrent execution, then a real-time version is made that captures the resources and configuration of the target hardware for which code will be written. VDM could be used to extend the model and capture true concurrency, allowing Designs 2, 3 and 4 (in Section 3.3) to be modelled.

In addition, extending the model to use VDM allows additional features of the Overture tool to be used. In the above model, tests were manual created and executed for all possible configurations. This is obviously cumbersome and unscalable for more complicated models. For VDM models, Overture has combinatorial testing features that allow tests to be created using regular expressions. Overture also has true unit testing for VDM models.

To extend the model to allow reconfiguration from Configuration 2 back to Configuration 1, the pre-condition on Reconfigure needs to be relaxed to permit future traces from both configurations (not just Configuration 2 in the current model). To extend the model to add further configurations, a few steps are necessary. First, the configuration must be defined as a value in the Configurations module (for example, Configuration3). Tests for this new configuration should be added to the Test module and executed. Finally, the pre-condition must be extended to consider traces of the new configuration to be valid. This is simple if it is acceptable to switch between any configuration at any time. However, the definitions would be more complicated if there were restrictions on reconfiguration. For example, if there are ‘points of no return’ in between different configurations.

In Section 4.2, extending the current model with a model of data was suggested. This extension represents an augmentation of the current workflow models with the data and operations necessary to allow customers to place orders. This could include data types for representing orders, such as the following:

CustId = token;
OrderId = token;
Order ::      custid : CustId
         inventoryOK : [bool]
            creditOK : [bool]
              accept : [bool];
state Office of
  orders : map OrderId to Order
end
Listing 27: Example data types for modelling the order system

The above defines identifiers for customers and orders using token types (a countably infinite set of distinct values that can be compared for equality and inequality). The Order type is a record that identifies a customer and the status of the order: whether or not the checks have been passed, and whether the order is accepted. The state of the model stores all orders in the orders map.

To continue this model, operations should be defined to receive and evaluate orders, and to accept or reject them, then to notify the customer, to bill and ship, and finally to archive. Each should manipulate the data model and be protected by pre-conditions to ensure consistency and make explicit any assumption about the system. The following implicit operation, defined only in terms of pre- and post-conditions, evaluates an order. The pre-condition states that the order must be known (in the domain of the orders state variable) and be unprocessed. The post-condition states that the orders map should contain an updated record for this order with the result of the inventory and credit check. The mu expression allows record values to be overwritten, rather than constructing a new copy with the mk_ constructor.

EvaluateOrder(oid:OrderId)
ext wr orders
pre oid in set dom orders and
    orders(oid).inventoryOK = nil and
    orders(oid).creditOK = nil and
    orders(oid).accept = nil
post exists iOK, cOK : bool & orders = orders~ ++
       {oid |-> mu(orders(oid),
         inventoryOK |-> iOK, creditOK |-> cOK)};
Listing 28: Example implicit operation to Evaluate an order

This extended data model could then be connected to the existing interpreter, such that when elements of the workflow are executed, calls are made to the operations that manipulate the data model. The extended model would demonstrate whether it was possible to build an actual order system that met the requirements, particularly when new configurations are introduced. The data and operations could then be used as a specification during implementation in some programming language.

5 Conditional Partial Order Graphs

Conditional partial order graphs (CPOGs) [Mok09] were originally introduced for reasoning about properties of families of graphs in the context of asynchronous microcontroller design [MY10] and processors with reconfigurable microarchitecture [MRSY14]. CPOGs are related to featured transition systems (FTSs) that are used to model feature families of software product lines [CHS10]. The key difference between FTSs and CPOGs is that FTSs use transition systems as the underlying formalism, whereas CPOGs are built on top of partial orders, which enables the modelling of true concurrency. In this paper, CPOGs are used to represent compactly graph families in which each graph expresses a workflow scenario (i.e. a particular collection of outcomes of branching actions of a workflow) or a particular collection of requirements on workflow actions and their order. For example, the requirements on Configuration 1 of the case study workflow (shown in Figure 3.1) can be expressed using a family of three simple (i.e. branch-free) graphs (see Figure 3). We use the term family instead of the more general term set to emphasise the fact that the graphs are annotated with branch decisions, in this case:

  • Inventory check OK: No

  • Inventory check OK: Yes, credit check OK: No

  • Inventory check OK: Yes, credit check OK: Yes

Figure 3: Expressing workflow requirements on Configuration 1 by a family of graphs (see Figure 3.1)

This can be expressed equivalently using the following predicates:

where InventoryCheck and CreditCheck are the names of the two branch actions. Henceforth, we adopt the predicate notation.

The remainder of this section is organized as follows. In Section 5.1 an axiomatic semantics of CPOGs is given and basic workflow modelling primitives are introduced, which are used in Section 5.2 to model the case study workflow and to demonstrate how CPOGs can be used for workflow verification by reduction to the Boolean Satisfiability (SAT) problem. An LTS semantics of CPOGs is defined in Section 5.3, thereby establishing a formal link with the other two formalisms discussed in this paper. Dynamic reconfiguration is discussed in Section 5.4, and we show that CPOGs use true concurrency semantics when modelling functional interference between workflow and reconfiguration actions. We comment on our experience of automating the verification of the workflow requirements in Section 5.5, and give an overall evaluation of the CPOG-based approach in Section 7.

5.1 Formalism

The axiomatic semantics of CPOGs is outlined below, and was first introduced in [MK14] to provide efficient compositionality and abstraction facilities for graphs. Each element of the algebra represents a family of graphs, and is defined as follows:

Let be the alphabet of names of actions (e.g. and ) that represent tasks and subtasks. Actions can have order dependencies between them. is used to construct models of workflows and to define their requirements using the following axioms:

  • The empty workflow is denoted by , that is, the empty family of graphs.

  • A workflow consisting of a single action is denoted simply by . It corresponds to a family consisting of a single graph that contains a single vertex .

  • The parallel composition of workflows and is denoted by   (e.g. the concurrent execution of Billing and Shipping shown in Figure 3.1 is denoted by ). The operator  is commutative, associative, and has as the identity:

    Intuitively, means ’execute workflows and concurrently, synchronising on common actions’.

  • The sequential composition of workflows and is denoted by   (e.g. the sequential execution of Shipping followed by Billing shown in Figure 3.1 is denoted by ). The operator  has a higher precedence than , is associative, has the same identity () as , distributes over , and can be decomposed into pairwise sequences:

    1. and

    2.  and 

    Intuitively, means ’execute workflow , then execute workflow ’. In other words, introduce order dependencies between all actions in and all actions in . Note that if and contain common actions they cannot be executed in sequence, as illustrated by an example below.

    Figure 4 shows an example of parallel and sequential composition of graphs. It can be seen that the parallel composition does not introduce any new order dependency between actions in different graphs; therefore, the actions can be executed concurrently. Sequential composition, on the other hand, imposes order on the actions by introducing new dependencies between actions , , and in the top graph and action in the bottom graph. Hence, the resulting workflow behaviour is interpreted as the behaviour specified by the top graph followed by the behaviour specified by the bottom graph. Another example of these operations is shown in Figure 5. Since the graphs have common vertices, their compositions are more complicated, in particular, their sequential composition contains the self-dependencies and which lead to a deadlock in the resulting workflow. Action can occur, but all the remaining actions are locked: neither nor can proceed due to the self-dependencies, while cannot proceed without and . Figures 6(a) and 6(b) illustrate the distributivity and decomposition axioms respectively.

  • A conditional workflow is denoted by , where is a predicate expressing a certain condition (e.g. ‘Inventory Check OK’) and is a workflow. Intuitively, means ’execute workflow only if condition holds’. We postulate that and . This allows us to model branching in flow charts. For example, the algebraic expression

    corresponds to a branching performed after action , which is followed by workflow if predicate holds, and by workflow if predicate does not hold. Alternatively, we can say that the above expression corresponds to a family of graphs, in which vertex is followed by actions from the graphs coming either from family or from family , as illustrated in Figure 6(c), where a dotted line between vertex and variable indicates that the variable is assigned a value during the execution of action . We will use the following short-hand notation for a clearer correspondence with flow charts:

    where predicate ‘A OK’ corresponds to the successful completion of a branching action . For example, the expression

    corresponds to the first branching in Figure 3.1: if the inventory check is completed successfully the workflow continues with the credit check, otherwise the order is rejected and the workflow ends (actions and denote respectively the start and end circles used in the flow chart). Notice that operators and bind less tightly than to reduce the number of parentheses. Operator has the highest precedence and has the following useful properties (proved in [MK14]):

    1.         2.  
    3.         4.  

Notice that the condition operator can be used not only to create conditional workflows, but also to create conditional workflow dependencies. For example, in the expression , if , the expression simplifies to the parallel composition , but if , the expression simplifies to the sequential composition . In other words, actions in conditionally depend on actions in .

To summarise, the following operators are used to create and manipulate graph families corresponding to workflows (in decreasing order of precedence):
, , , , and , where is an action, is a predicate, and and are workflows.

The algebraic notation is used to translate flow charts into mathematical descriptions amenable to automated verification, as described in the next subsection.

Figure 4: Parallel and sequential composition example (no common vertex)
Figure 5: Parallel and sequential composition example (common vertices)
(a) Distributivity
(b) Decomposition
(c) Branching graph family
(d) Transitive reduction/closure
Figure 6: Manipulating parameterised graphs

5.2 Modelling and Analysis

The granularity of concurrency in CPOGs is a single action, and the granularity of reconfiguration is a single action and a single dependency between actions. Multiple actions and dependencies can also be reconfigured by a single reconfiguration action that sets Boolean variables in the predicates that guard the actions and their dependencies. Thus, it is possible to model all three cases of dynamic reconfiguration identified in Section 1. A CPOG can easily express concurrent actions, and (therefore) Case 3 of reconfiguration fits the CPOG ’idiom’ best. However, we have modelled Case 1 of reconfiguration for simplicity. Notice that the different designs of the case study workflow configurations are represented as the same CPOG model, because CPOGs cannot represent cyclic processes.

We now describe how to specify workflow requirements and their reconfiguration as families of graphs, how to reason about the correctness of such specifications, and how to manipulate them using the operators of the algebra.

The algebraic notation can be used to translate the flow chart in Figure 3.1 into the following expression:

The expression can be rewritten using the axioms in order to prove that certain requirements hold. For example, any expression can be rewritten into a so-called canonical form [MK14], which is sufficient for checking most of the requirements listed in Section 3.1.

Proposition 1

Any workflow expression can be rewritten in the following canonical form [MK14]:

(1)

where:

  • is a subset of actions that appear in the original expression;

  • for all , are canonical forms of Boolean expressions and are distinct from 0;

  • for all , are canonical forms of Boolean expressions such that (that is, a dependency between actions and can exist only if both actions exist).

In other words, the canonical form of an expression lists all constituent actions and all pairwise dependencies between them (along with their predicates). The canonical form can contain redundant transitive dependencies, such as in presence of both and . Such terms can be eliminated to simplify the resulting expression. This corresponds to the well-known transitive reduction procedure, which can be formalised by adding the following axiom [MK14]:

The axiom can be used to add or to remove transitive dependencies as necessary, see Figure 6(d).

Rewriting a workflow expression manually by following the CPOG axioms is tedious and error-prone. This motivated us to automate computation of the canonical form and the transitive reduction, as will be discussed in Section 5.5. By applying these procedures to  we obtain its reduced canonical form shown below. For brevity, we will denote predicates InventoryCheck OK and CreditCheck OK simply by and respectively. Actions are visually separated from dependencies by a horizontal line.

The resulting expression gives us plenty of valuable information that can be used for analysis of the workflow. In particular, the following correctness properties can be verified:

  • The starting and ending actions are part of the workflow regardless of possible outcomes of the branching actions, as indicated by ‘unconditional’ terms and .

  • The billing, shipping, archiving, and confirmation actions are performed if and only if both the inventory and internal credit checks are successful; in fact, all these actions are pre-conditioned with the predicate .

  • An order is rejected if either the inventory check or the internal credit check fails, as confirmed by term .

  • The first action after is always , as confirmed by term and the fact that all other actions transitively depend on .

  • The shipping and billing actions are always performed sequentially: whenever the actions occur
    ( and ) so does the dependency .

  • There are no cyclic dependencies and therefore each action is performed at most once.

We now translate the workflow requirements for Configuration 2 shown in Figure 3.1 into our notation, verify several relevant correctness properties, and highlight the differences between the two configurations. The flow chart in Figure 3.1 can be translated into the following expression:

where expression corresponds to the part of the workflow starting with the action: