Skip to content

Model Life Cycle

Axel Habermaier edited this page Jun 17, 2016 · 1 revision

To analyze S# models, it is important to understand S#'s model life-cycle. Otherwise, it might be a bit confusing as to what is going on and what C# and S# language features are allowed to be used in which situations. The following table gives a brief overview of the different life-cycle phases and what happens during each of them; detailed descriptions follow below.

Phase Step Conducted By Remarks
Design Time Model Editing Modeler Specification of model element types and model instantiations in a S# project. Basically, S#-enriched C# code is written to model a safety-critical system and any required infrastructure code.
Build Time Diagnostics S# Analyzers, C# Compiler The S# project is checked for syntactic and semantic errors that violate any S# or C# rules.
Build Time Normalization S# Normalizers S#-specific model fragments contained in the S# project are transformed to regular C# code.
Build Time Compilation C# Compiler The S# project is compiled with the standard C# compiler to produce an executable .NET assembly.
Run Time Model Initialization User Code A S# ModelBase instance is initialized in some arbirary way that is of no interest to S#; Component constructors are executed. No nondeterminism is supported.
Run Time Model Copying S# Runtime A deep copy of the ModelBase instance is made. Caution: The next steps work on the copy, leaving the original instance unmodified.
Run Time Model Execution - Initial States S# Runtime Initial states of the model copy are computed. Nondeterminism is supported.
Run Time Model Execution - Model Steps S# Runtime The Update methods of the Component instances contained in the model copy are executed multiple times. Nondeterminism is supported.
Run Time Clean-Up .NET Garbage Collector The model copy is cleaned-up and garbage collected.

Phase 1: Design Time

During the design time phase, the modeler modifies the C# code constituting the S# model and all supporting infrastructure code. The latter might, for example, instantiate models and execute test cases.

S# models must be valid C# code; they must also satisfy all additional syntactic and semantic constraints imposed on them by S# such as not using certain unsupported language features or incompatible .NET libraries. For instance, S# models should not use random numbers or multi-threaded execution.

While infrastructure code must also be valid C# code, it can use all C# language features and all .NET libraries; it is simply executed to instantiate models and to start up simulations and model checking-based analyses; the code is otherwise of no interest to S#.

Phase 2: Build Time

Like all C# programs, S# models must be compiled to a .NET assembly before they can be executed, that is, before they can be simulated or model checked. S# extends the usual C# build process, adding two additional steps before the default compilation step. S#'s build process is fully integrated into MSBuild and Visual Studio, requiring no manual user interaction.

Phase 2.1: Diagnostics

S# comes with a set of analyzers that are injected into Visual Studio and the build process. At design time, the analyzers provide immediate diagnostics (i.e., warnings and errors) for any syntactical or semantical issues that are found in the model. At build time, these analyzers are also executed to ensure that no invalid models enter the normalization step of the build process.

Phase 2.2: Normalization

S# provides a set of code normalizers that normalize certain S#-specific constructs into regular, executable C# code. Normalizers transform the original model code for fault effects, state machines, and required ports, among others, into semantically equivalent C# code.

There are two reasons for these transformations: The transformations might either be required to implement the intended S# semantics, or they rewrite code to make its execution more efficient; the latter is of high importance for model checking, where the same line of code is potentially executed millions of times, if not more.

Phase 2.3: Compilation

The normalized code is finally passed to the standard C# compiler for compilation into a .NET assembly.

Phase 3: Run Time

At run time, a model is instantiated and executed for simulation or model checking purposes. The run time phase is divided into multiple separate steps:

Phase 3.1: Model Initialization

During the model initialization step, arbitrary C# code is executed that does not interact with S# other than by instantiating Component instances and a ModelBase instance. Constructors of the Component instances are executed. The

Caution: Nondeterminism via any of the Choose functions is not supported at this stage.

Phase 3.2: Model Copying

When the ModelBase instance is passed to a S# model checker or a S# simulator, a copy of the model is made. The original instance is neither modified nor used by the simulator or model checker. In fact, S# does not even hold on to a reference to the original model in any way. The underlying reasons is that model checkers by default use all available CPU cores to check a model; each core gets its own copy, avoiding any synchronization overhead between the individual cores while executing model code.

While model checking, the ModelBase copy is not accessible until a counter example is generated. For simulations, the copy can be retrieved from the simulator, allowing visualizations, for instance, to visualize the model state during simulation.

The ModelBase copy is a deep copy of the original model, copying the entire object graph of all .NET objects reachable from the original ModelBase instance. Not all .NET types can be copied; generally, primitive types such as int and bool as well as standard types like arrays and delegates are copyable. S# components and other .NET classes such as List<T> that make use of only these types are also copyable. The data of all objects is copied over; no constructors are run on the copies.

Caution: Once the copy is made, all changes to the original model obviously do not affect the copies. It is not possible to reference any newly allocated objects from the copies.

Phase 3.3: Model Execution

The ModelBase copy is executed either by a simulator or by a model checker. There are two steps involved:

Initial States Computation

Model execution starts by computing the initial states of the model; simulations compute only one such state, whereas model checkers compute all initial states. In the simplest case, there is only one initial state, namely the state of all objects directly after copying.

If an object wants to execute custom, possibly nondeterministic code to determine its initial states, it has to implement the IInitializable interface: The S# runtime calls the IInitializable.Initialize() method on all objects in the model copy that implement that interface. In particular, Component implements IInitializable and provides the virtual Component.Initialize() method for convenience. Component must therefore simply override Component.Initialize() to participate in the initial state computation.

Note: The family of nondeterministic Choose functions can be used during initial states computation.

Caution: Note that no port bindings should ever be established in Component.Initialize() as only one copy will see the new bindings when model checking.

Model Stepping

Once the initial state(s) are known, simulations repeatedly compute one successor state. Model checkers, by contrast, always compute all successor states that exist. There might be multiple successors when the family of nondeterministic Choose functions is used or faults are activated. For simulations, model stepping continues until the simulation is stopped. When model checking, on the other hand, model stepping is aborted as soon as all reachable states are discovered or a violation of the analyzed formula is detected.

Phase 3.4: Clean-Up

Once model execution is completed, i.e., the simulation is aborted or model checking is terminated, the ModelBase copies are eventually cleaned up by .NET's garbage collector.

Even though rarely used, custom clean-up is also supported, where objects are allowed to execute some code before being garbage collected. To do so, the objects the model copies consist of can implement .NET's standard Dispose Pattern. Note that the S# runtime does not call the Dispose methods of the objects contained in the original version of the analyzed model; S# does not keep any reference to original models after model copying.