summaryrefslogtreecommitdiff
path: root/sys/doc/spin.ms
diff options
context:
space:
mode:
authoraiju <aiju@phicode.de>2011-07-18 11:01:22 +0200
committeraiju <aiju@phicode.de>2011-07-18 11:01:22 +0200
commit8c4c1f39f4e369d7c590c9d119f1150a2215e56d (patch)
treecd430740860183fc01de1bc1ddb216ceff1f7173 /sys/doc/spin.ms
parent11bf57fb2ceb999e314cfbe27a4e123bf846d2c8 (diff)
added /sys/doc
Diffstat (limited to 'sys/doc/spin.ms')
-rw-r--r--sys/doc/spin.ms2519
1 files changed, 2519 insertions, 0 deletions
diff --git a/sys/doc/spin.ms b/sys/doc/spin.ms
new file mode 100644
index 000000000..5cfea4605
--- /dev/null
+++ b/sys/doc/spin.ms
@@ -0,0 +1,2519 @@
+.HTML "Using SPIN
+.\" runoff as:
+.\" eqn file | tbl | troff -ms
+.\"
+.ds P \\s-1PROMELA\\s0
+.ds V \\s-1SPIN\\s0
+.ds C pcc
+.\" .tr -\(hy
+.EQ
+delim $#
+.EN
+.TL
+Using \*V
+.AU
+Gerard J. Holzmann
+gerard@plan9.bell-labs.com
+.AB
+\*V can be used for proving or disproving logical properties
+of concurrent systems.
+To render the proofs, a concurrent system is first
+modeled in a formal specification language called \*P.
+The language allows one to specify the behaviors
+of asynchronously executing
+processes that may interact through synchronous
+or asynchronous message passing, or through direct
+access to shared variables.
+.LP
+System models specified in this way can be verified
+for both safety and liveness properties. The specification
+of general properties in linear time temporal logic is
+also supported.
+.LP
+The first part of this manual
+discusses the basic features of the specification language \*P.
+The second part describes the verifier \*V.
+.AE
+.NH 1
+The Language \*P
+.LP
+\*P is short for Protocol Meta Language [Ho91].
+\*P is a \f2modeling\f1 language, not a programming language.
+A formal model differs in two essential ways from an implementation.
+First, a model is meant to be an abstraction of a design
+that contains only those aspects of the design that are
+directly relevant to the properties one is interested in proving.
+Second, a formal model must contain things that are typically not part
+of an implementation, such as worst-case assumptions about
+the behavior of the environment that may interact with the
+system being studied, and a formal statement of relevant correctness
+properties. It is possible to mechanically extract abstract models
+from implementation level code, as discussed, for instance in [HS99].
+.LP
+Verification with \*V is often performed in a series of steps,
+with the construction of increasingly detailed models.
+Each model can be verified under different types of
+assumptions about the environment and for different
+types of correctness properties.
+If a property is not valid for the given assumptions about
+system behavior, the verifier can produce a counter-example
+that demonstrates how the property may be violated.
+If a property is valid, it may be possible to simplify the
+model based on that fact, and prove still other properties.
+.LP
+Section 1.1 covers the basic building blocks of the language.
+Section 1.2 introduces the control flow structures.
+Section 1.3 explains how correctness properties are specified.
+Section 1.4 concludes the first part with a discussion of
+special predefined variables and functions that can be used to
+express some correctness properties.
+.LP
+Up to date manual pages for \*V can always be found online at:
+.CW
+http://cm.bell-labs.com/cm/cs/what/spin/Man/
+.R
+.NH 2
+Basics
+.LP
+A \*P model can contain three different types of objects:
+.IP
+.RS
+\(bu Processes (section 1.1.1),
+.br
+\(bu Variables (section 1.1.2),
+.br
+\(bu Message channels (section 1.1.3).
+.RE
+.LP
+All processes are global objects.
+For obvious reasons, a \*P model must contain at least one
+process to be meaningful.
+Since \*V is specifically meant to prove properties of
+concurrent systems, a model typically contains more than
+one process.
+.LP
+Message channels and variables, the two basic types of data objects,
+can be declared with either a global scope or a local scope.
+A data object with global scope can be referred to by all processes.
+A data object with a local scope can be referred to by just a
+single process: the process that declares and instantiates the object.
+As usual, all objects must be declared in the specification
+before they are referenced.
+.NH 3
+Processes
+.LP
+Here is a simple process that does nothing except print
+a line of text:
+.P1
+init {
+ printf("it works\en")
+}
+.P2
+There are a few things to note.
+.CW Init
+is a predefined keyword from the language.
+It can be used to declare and instantiate
+a single initial process in the model.
+(It is comparable to the
+.CW main
+procedure of a C program.)
+The
+.CW init
+process does not take arguments, but it can
+start up (instantiate) other processes that do.
+.CW Printf
+is one of a few built-in procedures in the language.
+It behaves the same as the C version.
+Note, finally, that no semicolon follows the single
+.CW printf
+statement in the above example.
+In \*P, semicolons are used as statement separators,
+not statement terminators. (The \*V parser, however, is
+lenient on this issue.)
+.LP
+Any process can start new processes by using another
+built-in procedure called
+.CW run .
+For example,
+.P1
+proctype you_run(byte x)
+{
+ printf("my x is: %d\en", x)
+}
+.P2
+.P1
+init {
+ run you_run(1);
+ run you_run(2)
+}
+.P2
+The word
+.CW proctype
+is again a keyword that introduces the declaration
+of a new type of process.
+In this case, we have named that type
+.CW you_run
+and declared that all instantiations of processes
+of this type will take one argument: a data object
+of type
+.CW byte ,
+that can be referred to within this process by the name
+.CW x .
+Instances of a
+.CW proctype
+can be created with the predefined procedure
+.CW run ,
+as shown in the example.
+When the
+.CW run
+statement completes, a copy of the process
+has been started, and all its arguments have been
+initialized with the arguments provided.
+The process may, but need not, have performed
+any statement executions at this point.
+It is now part of the concurrent system,
+and its execution can be interleaved arbitrarily with
+those of the other, already executing processes.
+(More about the semantics of execution follows shortly.)
+.LP
+In many cases, we are only interested in creating a
+single instance of each process type that is declared,
+and the processes require no arguments.
+We can define this by prefixing the keyword
+.CW proctype
+from the process declaration with another keyword:
+.CW active .
+Instances of all active proctypes are created when the
+system itself is initialized.
+We could, for instance, have avoided the use of
+.CW init
+by declaring the corresponding process in the last example
+as follows:
+.P1
+active proctype main() {
+ run you_run(1);
+ run you_run(2)
+}
+.P2
+Note that there are no parameters to instantiate in this
+case. Had they been declared, they would default to a
+zero value, just like all other data objects
+that are not explicitly instantiated.
+.LP
+Multiple copies of a process type can also be created in
+this way. For example:
+.P1
+active [4] proctype try_me() {
+ printf("hi, i am process %d\en", _pid)
+}
+.P2
+creates four processes.
+A predefined variable
+.CW _pid
+is assigned to each running process, and holds
+its unique process instantiation number.
+In some cases, this number is needed when a reference
+has to be made to a specific process.
+.LP
+Summarizing: process behavior is declared in
+.CW proctype
+definitions, and it is instantiated with either
+.CW run
+statements or with the prefix
+.CW active .
+Within a proctype declaration, statements are separated
+(not terminated) by semicolons.
+As we shall see in examples that follow, instead of the
+semicolon, one can also use the alternative separator
+.CW "->"
+(arrow), wherever that may help to clarify the structure
+of a \*P model.
+.SH
+Semantics of Execution
+.LP
+In \*P there is no difference between a condition or
+expression and a statement.
+Fundamental to the semantics of the language is the
+notion of the \f2executability\f1 of statements.
+Statements are either executable or blocked.
+Executability is the basic means of enforcing
+synchronization between the processes in a distributed system.
+A process can wait for an event to happen by waiting
+for a statement to become executable.
+For instance, instead of writing a busy wait loop:
+.P1
+while (a != b) /* not valid Promela syntax */
+ skip; /* wait for a==b */
+\&...
+.P2
+we achieve the same effect in \*P with the statement
+.P1
+(a == b);
+\&...
+.P2
+Often we indicate that the continuation of an execution
+is conditional on the truth of some expression by using
+the alternate statement separator:
+.P1
+(a == b) -> \&...
+.P2
+Assignments and
+.CW printf
+statements are always executable in \*P.
+A condition, however, can only be executed (passed) when it holds.
+If the condition does not hold, execution blocks until it does.
+There are similar rules for determining the executability
+of all other primitive and compound statements in the
+language.
+The semantics of each statement is defined in terms of
+rules for executability and effect.
+The rules for executability set a precondition on the state
+of the system in which a statement can be executed.
+The effect defines how a statement will alter a
+system state when executed.
+.LP
+\*P assumes that all individual statements are executed
+atomically: that is, they model the smallest meaningful entities
+of execution in the system being studied.
+This means that \*P defines the standard asynchronous interleaving
+model of execution, where a supposed scheduler is free at
+each point in the execution to select any one of the processes
+to proceed by executing a single primitive statement.
+Synchronization constraints can be used to influence the
+interleaving patterns. It is the purpose of a concurrent system's
+design to constrain those patterns in such a way that no
+correctness requirements can be violated, and all service
+requirements are met. It is the purpose of the verifier
+either to find counter-examples to a designer's claim that this
+goal has been met, or to demonstrate that the claim is indeed valid.
+.NH 3
+Variables
+.LP
+The table summarizes the five basic data types used in \*P.
+.CW Bit
+and
+.CW bool
+are synonyms for a single bit of information.
+The first three types can store only unsigned quantities.
+The last two can hold either positive or negative values.
+The precise value ranges of variables of types
+.CW short
+and
+.CW int
+is implementation dependent, and corresponds
+to those of the same types in C programs
+that are compiled for the same hardware.
+The values given in the table are most common.
+.KS
+.TS
+center;
+l l
+lw(10) lw(12).
+=
+\f3Type Range\f1
+_
+bit 0..1
+bool 0..1
+byte 0..255
+short $-2 sup 15# .. $2 sup 15 -1#
+int $-2 sup 31# .. $2 sup 31 -1#
+_
+.TE
+.KE
+.LP
+The following example program declares a array of
+two elements of type
+.CW bool
+and a scalar variable
+.CW turn
+of the same type.
+Note that the example relies on the fact that
+.CW _pid
+is either 0 or 1 here.
+.MT _sec5critical
+.P1
+/*
+ * Peterson's algorithm for enforcing
+ * mutual exclusion between two processes
+ * competing for access to a critical section
+ */
+bool turn, want[2];
+
+active [2] proctype user()
+{
+again:
+ want[_pid] = 1; turn = _pid;
+
+ /* wait until this condition holds: */
+ (want[1 - _pid] == 0 || turn == 1 - _pid);
+
+ /* enter */
+critical: skip;
+ /* leave */
+
+ want[_pid] = 0;
+ goto again
+}
+.P2
+In the above case, all variables are initialized to zero.
+The general syntax for declaring and instantiating a
+variable, respectively for scalar and array variables, is:
+.P1
+type name = expression;
+type name[constant] = expression
+.P2
+In the latter case, all elements of the array are initialized
+to the value of the expression.
+A missing initializer fields defaults to the value zero.
+As usual, multiple variables of the same type can be grouped
+behind a single type name, as in:
+.P1
+byte a, b[3], c = 4
+.P2
+In this example, the variable
+.CW c
+is initialized to the value 4; variable
+.CW a
+and the elements of array
+.CW b
+are all initialized to zero.
+.LP
+Variables can also be declared as structures.
+For example:
+.P1
+typedef Field {
+ short f = 3;
+ byte g
+};
+
+typedef Msg {
+ byte a[3];
+ int fld1;
+ Field fld2;
+ chan p[3];
+ bit b
+};
+
+Msg foo;
+.P2
+introduces two user-defined data types, the first named
+.CW Field
+and the second named
+.CW Msg .
+A single variable named
+.CW foo
+of type
+.CW Msg
+is declared.
+All fields of
+.CW foo
+that are not explicitly initialized (in the example, all fields except
+.CW foo.fld2.f )
+are initialized to zero.
+References to the elements of a structure are written as:
+.P1
+foo.a[2] = foo.fld2.f + 12
+.P2
+A variable of a user-defined type can be passed as a single
+argument to a new process in
+.CW run
+statements.
+For instance,
+.P1
+proctype me(Msg z) {
+ z.a[2] = 12
+}
+init {
+ Msg foo;
+ run me(foo)
+}
+.P2
+.LP
+Note that even though \*P supports only one-dimensional arrays,
+a two-dimensional array can be created indirectly with user-defined
+structures, for instance as follows:
+.P1
+typedef Array {
+ byte el[4]
+};
+
+Array a[4];
+.P2
+This creates a data structure of 16 elements that can be
+referenced, for instance, as
+.CW a[i].el[j] .
+.LP
+As in C, the indices of an array of
+.CW N
+elements range from 0 to
+.CW N-1 .
+.SH
+Expressions
+.LP
+Expressions must be side-effect free in \*P.
+Specifically, this means that an expression cannot
+contain assignments, or send and receive operations (see section 1.1.3).
+.P1
+c = c + 1; c = c - 1
+.P2
+and
+.P1
+c++; c--
+.P2
+are assignments in \*P, with the same effects.
+But, unlike in C,
+.P1
+b = c++
+.P2
+is not a valid assignment, because the right-hand side
+operand is not a valid expression in \*P (it is not side-effect free).
+.LP
+It is also possible to write a side-effect free conditional
+expression, with the following syntax:
+.P1
+(expr1 -> expr2 : expr3)
+.P2
+The parentheses around the conditional expression are required to
+avoid misinterpretation of the arrow.
+The example expression has the value of \f(CWexpr2\f1 when \f(CWexpr1\f1
+evaluates to a non-zero value, and the value of \f(CWexpr3\f1 otherwise.
+.LP
+In assignments like
+.P1
+variable = expression
+.P2
+the values of all operands used inside the expression are first cast to
+signed integers before the operands are applied.
+After the evaluation of the expression completes, the value produced
+is cast to the type of the target variable before the assignment takes place.
+.NH 3
+Message Channels
+.LP
+Message channels are used to model the transfer of data
+between processes.
+They are declared either locally or globally,
+for instance as follows:
+.P1
+chan qname = [16] of { short, byte }
+.P2
+The keyword
+.CW chan
+introduces a channel declaration.
+In this case, the channel is named
+.CW qname ,
+and it is declared to be capable of storing up
+to 16 messages.
+Each message stored in the channel is declared here to
+consist of two fields: one of type
+.CW short
+and one of type
+.CW byte .
+The fields of a message can be any one of the basic types
+.CW bit ,
+.CW bool ,
+.CW byte ,
+.CW short ,
+.CW int ,
+and
+.CW chan ,
+or any user-defined type.
+Message fields cannot be declared as arrays.
+.LP
+A message field of type
+.CW chan
+can be used to pass a channel identifier
+through a channel from one process to another.
+.LP
+The statement
+.P1
+qname!expr1,expr2
+.P2
+sends the values of expressions
+.CW expr1
+and
+.CW expr2
+to the channel that we just created. It appends
+the message field created from the values of the two
+expressions (and cast to the appropriate types of the
+message fields declared for
+.CW qname )
+to the tail of the message buffer of 16 slots that belongs
+to channel
+.CW qname .
+By default the send statement is only executable if the target
+channel is non-full.
+(This default semantics can be changed in the verifier into
+one where the send statement is always executable, but the
+message will be lost when an attempt is made to append it to
+a full channel.)
+.LP
+The statement
+.P1
+qname?var1,var2
+.P2
+retrieves a message from the head of the same buffer,
+and stores the two expressions in variables
+.CW var1
+and
+.CW var2 .
+.LP
+The receive statement is executable only if the source channel
+is non-empty.
+.LP
+If more parameters are sent per message than were declared
+for the message channel, the redundant parameters are lost.
+If fewer parameters are sent than declared,
+the value of the remaining parameters is undefined.
+Similarly, if the receive operation tries to retrieve more
+parameters than available, the value of the extra parameters is
+undefined; if it receives fewer than the number of parameters
+sent, the extra information is lost.
+.LP
+An alternative, and equivalent, notation for the
+send and receive operations is to structure the
+message fields with parentheses, as follows:
+.P1
+qname!expr1(expr2,expr3)
+qname?var1(var2,var3)
+.P2
+In the above case, we assume that
+.CW qname
+was declared to hold messages consisting of three fields.
+.PP
+Some or all of the arguments of the receive operation
+can be given as constants instead of as variables:
+.P1
+qname?cons1,var2,cons2
+.P2
+In this case, an extra condition on the executability of the
+receive operation is that the value of all message fields
+specified as constants match the value of the corresponding
+fields in the message that is to be received.
+.LP
+Here is an example that uses some of the mechanisms introduced
+so far.
+.P1
+proctype A(chan q1)
+{ chan q2;
+ q1?q2;
+ q2!123
+}
+.P2
+.P1
+proctype B(chan qforb)
+{ int x;
+ qforb?x;
+ printf("x = %d\en", x)
+}
+.P2
+.P1
+init {
+ chan qname = [1] of { chan };
+ chan qforb = [1] of { int };
+ run A(qname);
+ run B(qforb);
+ qname!qforb
+}
+.P2
+The value printed by the process of type
+.CW B
+will be
+.CW 123 .
+.LP
+A predefined function
+.CW len(qname)
+returns the number of messages currently
+stored in channel
+.CW qname .
+Two shorthands for the most common uses of this
+function are
+.CW empty(qname)
+and
+.CW full(qname) ,
+with the obvious connotations.
+.LP
+Since all expressions must be side-effect free,
+it is not valid to say:
+.P1
+(qname?var == 0)
+.P2
+or
+.P1
+(a > b && qname!123)
+.P2
+We could rewrite the second example (using an atomic sequence,
+as explained further in section 1.2.1):
+.P1
+atomic { (a > b && !full(qname)) -> qname!123 }
+.P2
+The meaning of the first example is ambiguous. It could mean
+that we want the condition to be true if the receive operation
+is unexecutable. In that case, we can rewrite it without
+side-effects as:
+.P1
+empty(qname)
+.P2
+It could also mean that we want the condition
+to be true when the channel does contain a message with
+value zero.
+We can specify that as follows:
+.P1
+atomic { qname?[0] -> qname?var }
+.P2
+The first statement of this atomic sequence is
+an expression without side-effects that
+evaluates to a non-zero value only if the
+receive operation
+.P1
+qname?0
+.P2
+would have been executable at that
+point (i.e., channel
+.CW qname
+contains at least one message and the oldest
+message stored consists of one message field
+equal to zero).
+Any receive statement can be turned into
+a side-effect free expression by placing square
+brackets around the list of all message parameters.
+The channel contents remain undisturbed by the
+evaluation of such expressions.
+.LP
+Note carefully, however, that in non-atomic sequences
+of two statements such as
+.P1
+!full(qname) -> qname!msgtype
+.P2
+and
+.P1
+qname?[msgtype] -> qname?msgtype
+.P2
+the second statement is not necessarily executable
+after the first one has been executed.
+There may be race conditions when access to the channels
+is shared between several processes.
+Another process can send a message to the channel
+just after this process determined that it was not full,
+or another process can steal away the
+message just after our process determined its presence.
+.LP
+Two other types of send and receive statements are used
+less frequently: sorted send and random receive.
+A sorted send operation is written with two, instead of one,
+exclamation marks, as follows:
+.P1
+qname!!msg
+.P2
+A sorted send operation will insert a message into the channel's buffer
+in numerical order, instead of in FIFO order.
+The channel contents are scanned from the first message towards the
+last, and the message is inserted immediately before the first message
+that follows it in numerical order.
+To determine the numerical order, all message fields are
+taken into account.
+.LP
+The logical counterpart of the sorted send operation
+is the random receive.
+It is written with two, instead of one, question marks:
+.P1
+qname??msg
+.P2
+A random receive operation is executable if it is executable for \f2any\f1
+message that is currently buffered in a message channel (instead of
+only for the first message in the channel).
+Normal send and receive operations can freely be combined with
+sorted send and random receive operations.
+.SH
+Rendezvous Communication
+.LP
+So far we have talked about asynchronous communication between processes
+via message channels, declared in statements such as
+.P1
+chan qname = [N] of { byte }
+.P2
+where
+.CW N
+is a positive constant that defines the buffer size.
+A logical extension is to allow for the declaration
+.P1
+chan port = [0] of { byte }
+.P2
+to define a rendezvous port.
+The channel size is zero, that is, the channel
+.CW port
+can pass, but cannot store, messages.
+Message interactions via such rendezvous ports are
+by definition synchronous.
+Consider the following example:
+.P1
+#define msgtype 33
+
+chan name = [0] of { byte, byte };
+
+active proctype A()
+{ name!msgtype(124);
+ name!msgtype(121)
+}
+.P2
+.P1
+active proctype B()
+{ byte state;
+ name?msgtype(state)
+}
+.P2
+Channel
+.CW name
+is a global rendezvous port.
+The two processes will synchronously execute their first statement:
+a handshake on message
+.CW msgtype
+and a transfer of the value 124 to local variable
+.CW state .
+The second statement in process
+.CW A
+will be unexecutable,
+because there is no matching receive operation in process
+.CW B .
+.LP
+If the channel
+.CW name
+is defined with a non-zero buffer capacity,
+the behavior is different.
+If the buffer size is at least 2, the process of type
+.CW A
+can complete its execution, before its peer even starts.
+If the buffer size is 1, the sequence of events is as follows.
+The process of type
+.CW A
+can complete its first send action, but it blocks on the
+second, because the channel is now filled to capacity.
+The process of type
+.CW B
+can then retrieve the first message and complete.
+At this point
+.CW A
+becomes executable again and completes,
+leaving its last message as a residual in the channel.
+.LP
+Rendezvous communication is binary: only two processes,
+a sender and a receiver, can be synchronized in a
+rendezvous handshake.
+.LP
+As the example shows, symbolic constants can be defined
+with preprocessor macros using
+.CW #define .
+The source text of a \*P model is translated by the standard
+C preprocessor.
+The disadvantage of defining symbolic names in this way is,
+however, that the \*P parser will only see the expanded text,
+and cannot refer to the symbolic names themselves.
+To prevent that, \*P also supports another way to define
+symbolic names, which are preserved in error reports.
+For instance, by including the declaration
+.P1
+mtype = { ack, msg, error, data };
+.P2
+at the top of a \*P model, the names provided between the
+curly braces are equivalent to integers of type
+.CW byte ,
+but known by their symbolic names to the \*V parser and the
+verifiers it generates.
+The constant values assigned start at 1, and count up.
+There can be only one
+.CW mtype
+declaration per model.
+.NH 2
+Control Flow
+.LP
+So far, we have seen only some of the basic statements
+of \*P, and the way in which they can be combined to
+model process behaviors.
+The five types of statements we have mentioned are:
+.CW printf ,
+.CW assignment ,
+.CW condition ,
+.CW send ,
+and
+.CW receive .
+.LP
+The pseudo-statement
+.CW skip
+is syntactically and semantically equivalent to the
+condition
+.CW (1)
+(i.e., to true), and is in fact quietly replaced with this
+expression by the lexical analyzer of \*V.
+.LP
+There are also five types of compound statements.
+.IP
+.RS
+\(bu
+Atomic sequences (section 1.2.1),
+.br
+\(bu
+Deterministic steps (section 1.2.2),
+.br
+\(bu
+Selections (section 1.2.3),
+.br
+\(bu
+Repetitions (section 1.2.4),
+.br
+\(bu
+Escape sequences (section 1.2.5).
+.RE
+.LP
+.NH 3
+Atomic Sequences
+.LP
+The simplest compound statement is the
+.CW atomic
+sequence:
+.P1
+atomic { /* swap the values of a and b */
+ tmp = b;
+ b = a;
+ a = tmp
+}
+.P2
+In the example, the values of two variables
+.CW a
+and
+.CW b
+are swapped in a sequence of statement executions
+that is defined to be uninterruptable.
+That is, in the interleaving of process executions, no
+other process can execute statements from the moment that
+the first statement of this sequence begins to execute until
+the last one has completed.
+.LP
+It is often useful to use
+.CW atomic
+sequences to start a series of processes in such a
+way that none of them can start executing statements
+until all of them have been initialized:
+.P1
+init {
+ atomic {
+ run A(1,2);
+ run B(2,3);
+ run C(3,1)
+ }
+}
+.P2
+.CW Atomic
+sequences may be non-deterministic.
+If any statement inside an
+.CW atomic
+sequence is found to be unexecutable, however,
+the atomic chain is broken, and another process can take over
+control.
+When the blocking statement becomes executable later,
+control can non-deterministically return to the process,
+and the atomic execution of the sequence resumes as if
+it had not been interrupted.
+.NH 3
+Deterministic Steps
+.LP
+Another way to define an indivisible sequence of actions
+is to use the
+.CW d_step
+statement.
+In the above case, for instance, we could also have written:
+.P1
+d_step { /* swap the values of a and b */
+ tmp = b;
+ b = a;
+ a = tmp
+}
+.P2
+The difference between a
+.CW d_step
+sequence
+and an
+.CW atomic
+sequence are:
+.IP \(bu
+A
+.CW d_step
+sequence must be completely deterministic.
+(If non-determinism is nonetheless encountered,
+it is always resolved in a fixed and deterministic
+way: i.e., the first true guard in selection or
+repetition structures is always selected.)
+.IP \(bu
+No
+.CW goto
+jumps into or out of a
+.CW d_step
+sequence are permitted.
+.IP \(bu
+The execution of a
+.CW d_step
+sequence cannot be interrupted when a
+blocking statement is encountered.
+It is an error if any statement other than
+the first one in a
+.CW d_step
+sequence is found to be unexecutable.
+.IP \(bu
+A
+.CW d_step
+sequence is executed as one single statement.
+In a way, it is a mechanism for adding new types
+of statements to the language.
+.LP
+None of the items listed above apply to
+.CW atomic
+sequences.
+This means that the keyword
+.CW d_step
+can always be replaced with the keyword
+.CW atomic ,
+but the reverse is not true.
+(The main, perhaps the only, reason for using
+.CW d_step
+sequences is to improve the efficiency of
+verifications.)
+.NH 3
+Selection Structures
+.LP
+A more interesting construct is the selection structure.
+Using the relative values of two variables
+.CW a
+and
+.CW b
+to choose between two options, for instance, we can write:
+.P1
+if
+:: (a != b) -> option1
+:: (a == b) -> option2
+fi
+.P2
+The selection structure above contains two execution sequences,
+each preceded by a double colon.
+Only one sequence from the list will be executed.
+A sequence can be selected only if its first statement is executable.
+The first statement is therefore called a \f2guard\f1.
+.LP
+In the above example the guards are mutually exclusive, but they
+need not be.
+If more than one guard is executable, one of the corresponding sequences
+is selected nondeterministically.
+If all guards are unexecutable the process will block until at least
+one of them can be selected.
+There is no restriction on the type of statements that can be used
+as a guard: it may include sends or receives, assignments,
+.CW printf ,
+.CW skip ,
+etc.
+The rules of executability determine in each case what the semantics
+of the complete selection structure will be.
+The following example, for instance, uses receive statements
+as guards in a selection.
+.P1
+mtype = { a, b };
+
+chan ch = [1] of { byte };
+
+active proctype A()
+{ ch!a
+}
+.P2
+.P1
+active proctype B()
+{ ch!b
+}
+.P2
+.P1
+active proctype C()
+{ if
+ :: ch?a
+ :: ch?b
+ fi
+}
+.P2
+The example defines three processes and one channel.
+The first option in the selection structure of the process
+of type
+.CW C
+is executable if the channel contains
+a message named
+.CW a ,
+where
+.CW a
+is a symbolic constant defined in the
+.CW mtype
+declaration at the start of the program.
+The second option is executable if it contains a message
+.CW b ,
+where, similarly,
+.CW b
+is a symbolic constant.
+Which message will be available depends on the unknown
+relative speeds of the processes.
+.LP
+A process of the following type will either increment
+or decrement the value of variable
+.CW count
+once.
+.P1
+byte count;
+
+active proctype counter()
+{
+ if
+ :: count++
+ :: count--
+ fi
+}
+.P2
+Assignments are always executable, so the choice made
+here is truly a non-deterministic one that is independent
+of the initial value of the variable (zero in this case).
+.NH 3
+Repetition Structures
+.LP
+We can modify the above program as follows, to obtain
+a cyclic program that randomly changes the value of
+the variable up or down, by replacing the selection
+structure with a repetition.
+.P1
+byte count;
+
+active proctype counter()
+{
+ do
+ :: count++
+ :: count--
+ :: (count == 0) -> break
+ od
+}
+.P2
+Only one option can be selected for execution at a time.
+After the option completes, the execution of the structure
+is repeated.
+The normal way to terminate the repetition structure is
+with a
+.CW break
+statement.
+In the example, the loop can be
+broken only when the count reaches zero.
+Note, however, that it need not terminate since the other
+two options remain executable.
+To force termination we could modify the program as follows.
+.P1
+active proctype counter()
+{
+ do
+ :: (count != 0) ->
+ if
+ :: count++
+ :: count--
+ fi
+ :: (count == 0) -> break
+ od
+}
+.P2
+A special type of statement that is useful in selection
+and repetition structures is the
+.CW else
+statement.
+An
+.CW else
+statement becomes executable only if no other statement
+within the same process, at the same control-flow point,
+is executable.
+We could try to use it in two places in the above example:
+.P1
+active proctype counter()
+{
+ do
+ :: (count != 0) ->
+ if
+ :: count++
+ :: count--
+ :: else
+ fi
+ :: else -> break
+ od
+}
+.P2
+The first
+.CW else ,
+inside the nested selection structure, can never become
+executable though, and is therefore redundant (both alternative
+guards of the selection are assignments, which are always
+executable).
+The second usage of the
+.CW else ,
+however, becomes executable exactly when
+.CW "!(count != 0)"
+or
+.CW "(count == 0)" ,
+and is therefore equivalent to the latter to break from the loop.
+.LP
+There is also an alternative way to exit the do-loop, without
+using a
+.CW break
+statement: the infamous
+.CW goto .
+This is illustrated in the following implementation of
+Euclid's algorithm for finding the greatest common divisor
+of two non-zero, positive numbers:
+.P1
+proctype Euclid(int x, y)
+{
+ do
+ :: (x > y) -> x = x - y
+ :: (x < y) -> y = y - x
+ :: (x == y) -> goto done
+ od;
+done:
+ skip
+}
+.P2
+.P1
+init { run Euclid(36, 12) }
+.P2
+The
+.CW goto
+in this example jumps to a label named
+.CW done .
+Since a label can only appear before a statement,
+we have added the dummy statement
+.CW skip .
+Like a
+.CW skip ,
+a
+.CW goto
+statement is always executable and has no other
+effect than to change the control-flow point
+of the process that executes it.
+.LP
+As a final example, consider the following implementation of
+a Dijkstra semaphore, which is implemented with the help of
+a synchronous channel.
+.P1
+#define p 0
+#define v 1
+
+chan sema = [0] of { bit };
+.P2
+.P1
+active proctype Dijkstra()
+{ byte count = 1;
+
+ do
+ :: (count == 1) ->
+ sema!p; count = 0
+ :: (count == 0) ->
+ sema?v; count = 1
+ od
+}
+.P2
+.P1
+active [3] proctype user()
+{ do
+ :: sema?p;
+ /* critical section */
+ sema!v;
+ /* non-critical section */
+ od
+}
+.P2
+The semaphore guarantees that only one of the three user processes
+can enter its critical section at a time.
+It does not necessarily prevent the monopolization of
+the access to the critical section by one of the processes.
+.LP
+\*P does not have a mechanism for defining functions or
+procedures. Where necessary, though, these may be
+modeled with the help of additional processes.
+The return value of a function, for instance, can be passed
+back to the calling process via global variables or messages.
+The following program illustrates this by recursively
+calculating the factorial of a number
+.CW n .
+.P1
+proctype fact(int n; chan p)
+{ chan child = [1] of { int };
+ int result;
+
+ if
+ :: (n <= 1) -> p!1
+ :: (n >= 2) ->
+ run fact(n-1, child);
+ child?result;
+ p!n*result
+ fi
+}
+.P2
+.P1
+init
+{ chan child = [1] of { int };
+ int result;
+
+ run fact(7, child);
+ child?result;
+ printf("result: %d\en", result)
+}
+.P2
+Each process creates a private channel and uses it
+to communicate with its direct descendant.
+There are no input statements in \*P.
+The reason is that models must always be complete to
+allow for logical verifications, and input statements
+would leave at least the source of some information unspecified.
+A way to read input
+would presuppose a source of information that is not
+part of the model.
+.LP
+We have already discussed a few special types of statement:
+.CW skip ,
+.CW break ,
+and
+.CW else .
+Another statement in this class is the
+.CW timeout .
+The
+.CW timeout
+is comparable to a system level
+.CW else
+statement: it becomes executable if and only if no other
+statement in any of the processes is executable.
+.CW Timeout
+is a modeling feature that provides for an escape from a
+potential deadlock state.
+The
+.CW timeout
+takes no parameters, because the types of properties we
+would like to prove for \*P models must be proven independent
+of all absolute and relative timing considerations.
+In particular, the relative speeds of processes can never be
+known with certainty in an asynchronous system.
+.NH 3
+Escape Sequences
+.LP
+The last type of compound structure to be discussed is the
+.CW unless
+statement.
+It is used as follows:
+.MT _sec5unless
+.P1
+{ P } unless { E }
+.P2
+where the letters
+.CW P
+and
+.CW E
+represent arbitrary \*P fragments.
+Execution of the
+.CW unless
+statement begins with the execution of statements from
+.CW P .
+Before each statement execution in
+.CW P
+the executability of the first statement of
+.CW E
+is checked, using the normal \*P semantics of executability.
+Execution of statements from
+.CW P
+proceeds only while the first statement of
+.CW E
+remains unexecutable.
+The first time that this `guard of the escape sequence'
+is found to be executable, control changes to it,
+and execution continues as defined for
+.CW E .
+Individual statement executions remain indivisible,
+so control can only change from inside
+.CW P
+to the start of
+.CW E
+in between individual statement executions.
+If the guard of the escape sequence
+does not become executable during the
+execution of
+.CW P ,
+then it is skipped entirely when
+.CW P
+terminates.
+.LP
+An example of the use of escape sequences is:
+.P1
+A;
+do
+:: b1 -> B1
+:: b2 -> B2
+\&...
+od
+unless { c -> C };
+D
+.P2
+As shown in the example, the curly braces around the main sequence
+(or the escape sequence) can be deleted if there can be no confusion
+about which statements belong to those sequences.
+In the example, condition
+.CW c
+acts as a watchdog on the repetition construct from the main sequence.
+Note that this is not necessarily equivalent to the construct
+.P1
+A;
+do
+:: b1 -> B1
+:: b2 -> B2
+\&...
+:: c -> break
+od;
+C; D
+.P2
+if
+.CW B1
+or
+.CW B2
+are non-empty.
+In the first version of the example, execution of the iteration can
+be interrupted at \f2any\f1 point inside each option sequence.
+In the second version, execution can only be interrupted at the
+start of the option sequences.
+.NH 2
+Correctness Properties
+.LP
+There are three ways to express correctness properties in \*P,
+using:
+.IP
+.RS
+.br
+\(bu
+Assertions (section 1.3.1),
+.br
+\(bu
+Special labels (section 1.3.2),
+.br
+\(bu
+.CW Never
+claims (section 1.3.3).
+.RE
+.LP
+.NH 3
+Assertions
+.LP
+Statements of the form
+.P1
+assert(expression)
+.P2
+are always executable.
+If the expression evaluates to a non-zero value (i.e., the
+corresponding condition holds), the statement has no effect
+when executed.
+The correctness property expressed, though, is that it is
+impossible for the expression to evaluate to zero (i.e., for
+the condition to be false).
+A failing assertion will cause execution to be aborted.
+.NH 3
+Special Labels
+.LP
+Labels in a \*P specification ordinarily serve as
+targets for unconditional
+.CW goto
+jumps, as usual.
+There are, however, also three types of labels that
+have a special meaning to the verifier.
+We discuss them in the next three subsections.
+.NH 4
+End-State Labels
+.LP
+When a \*P model is checked for reachable deadlock states
+by the verifier, it must be able to distinguish valid \f2end state\f1s
+from invalid ones.
+By default, the only valid end states are those in which
+every \*P process that was instantiated has reached the end of
+its code.
+Not all \*P processes, however, are meant to reach the
+end of their code.
+Some may very well linger in a known wait
+state, or they may sit patiently in a loop
+ready to spring into action when new input arrives.
+.LP
+To make it clear to the verifier that these alternate end states
+are also valid, we can define special end-state labels.
+We can do so, for instance, in the process type
+.CW Dijkstra ,
+from an earlier example:
+.P1
+proctype Dijkstra()
+{ byte count = 1;
+
+end: do
+ :: (count == 1) ->
+ sema!p; count = 0
+ :: (count == 0) ->
+ sema?v; count = 1
+ od
+}
+.P2
+The label
+.CW end
+defines that it is not an error if, at the end of an
+execution sequence, a process of this type
+has not reached its closing curly brace, but waits at the label.
+Of course, such a state could still be part of a deadlock state, but
+if so, it is not caused by this particular process.
+.LP
+There may be more than one end-state label per \*P model.
+If so, all labels that occur within the same process body must
+be unique.
+The rule is that every label name with the prefix
+.CW end
+is taken to be an end-state label.
+.NH 4
+Progress-State Labels
+.LP
+In the same spirit, \*P also allows for the definition of
+.CW progress
+labels.
+Passing a progress label during an execution is interpreted
+as a good thing: the process is not just idling while
+waiting for things to happen elsewhere, but is making
+effective progress in its execution.
+The implicit correctness property expressed here is that any
+infinite execution cycle allowed by the model that does not
+pass through at least one of these progress labels is a
+potential starvation loop.
+In the
+.CW Dijkstra
+example, for instance, we can label the
+successful passing of a semaphore test as progress and
+ask a verifier to make sure that there is no cycle elsewhere
+in the system.
+.P1
+proctype Dijkstra()
+{ byte count = 1;
+
+end: do
+ :: (count == 1) ->
+progress: sema!p; count = 0
+ :: (count == 0) ->
+ sema?v; count = 1
+ od
+}
+.P2
+If more than one state carries a progress label,
+variations with a common prefix are again valid.
+.NH 4
+Accept-State Labels
+.LP
+The last type of label, the accept-state label, is used
+primarily in combination with
+.CW never
+claims.
+Briefly, by labeling a state with any label starting
+with the prefix
+.CW accept
+we can ask the verifier to find all cycles that \f2do\f1
+pass through at least one of those labels.
+The implicit correctness claim is that this cannot happen.
+The primary place where accept labels are used is inside
+.CW never
+claims.
+We discuss
+.CW never
+claims next.
+.NH 3
+Never Claims
+.LP
+Up to this point we have talked about the specification
+of correctness criteria with assertions
+and with three special types of labels.
+Powerful types of correctness criteria can already
+be expressed with these tools, yet so far our only option is
+to add them to individual
+.CW proctype
+declarations.
+We can, for instance, express the claim ``every system state
+in which property
+.CW P
+is true eventually leads to a system state in which property
+.CW Q
+is true,'' with an extra monitor process, such as:
+.P1
+active proctype monitor()
+{
+progress:
+ do
+ :: P -> Q
+ od
+}
+.P2
+If we require that property
+.CW P
+must \f2remain\f1 true while we are waiting
+.CW Q
+to become true, we can try to change this to:
+.P1
+active proctype monitor()
+{
+progress:
+ do
+ :: P -> assert(P || Q)
+ od
+}
+.P2
+but this does not quite do the job.
+Note that we cannot make any assumptions about the
+relative execution speeds of processes in a \*P model.
+This means that if in the remainder of the system the
+property
+.CW P
+becomes true, we can move to the state just before the
+.CW assert ,
+and wait there for an unknown amount of time (anything between
+a zero delay and an infinite delay is possible here, since
+no other synchronizations apply).
+If
+.CW Q
+becomes true, we may pass the assertion, but we need not
+do so.
+Even if
+.CW P
+becomes false only \f2after\f1
+.CW Q
+has become true, we may still fail the assertion,
+as long as there exists some later state where neither
+.CW P
+nor
+.CW Q
+is true.
+This is clearly unsatisfactory, and we need another mechanism
+to express these important types of liveness properties.
+.SH
+The Connection with Temporal Logic
+.LP
+A general way to express system properties of the type we
+have just discussed is to use linear time temporal logic (LTL)
+formulae.
+Every \*P expression is automatically also a valid LTL formula.
+An LTL formula can also contain the unary temporal operators □
+(pronounced always), ◊ (pronounced eventually), and
+two binary temporal operators
+.CW U
+(pronounced weak until) and
+.BI U
+(pronounced strong until).
+.LP
+Where the value of a \*P expression without temporal operators can be
+defined uniquely for individual system states, without further context,
+the truth value of an LTL formula is defined for sequences of states:
+specifically, it is defined for the first state of a given infinite
+sequence of system states (a trace).
+Given, for instance, the sequence of system states:
+.P1
+s0;s1;s2;...
+.P2
+the LTL formula
+.CW pUq ,
+with
+.CW p
+and
+.CW q
+standard \*P expressions, is true for
+.CW s0
+either if
+.CW q
+is true in
+.CW s0 ,
+or if
+.CW p
+is true in
+.CW s0
+and
+.CW pUq
+holds for the remainder of the sequence after
+.CW s0 .
+.LP
+Informally,
+.CW pUq
+says that
+.CW p
+is required to hold at least until
+.CW q
+becomes true.
+If, instead, we would write \f(CWp\f(BIU\f(CWq\f1,
+then we also require that there exists at least
+one state in the sequence where
+.CW q
+does indeed become true.
+.LP
+The temporal operators □ and ◊
+can be defined in terms of the strong until operator
+.BI U ,
+as follows.
+.P1
+□ p = !◊ !p = !(true \f(BIU\f(CW !p)
+.P2
+Informally, □
+.CW p
+says that property
+.CW p
+must hold in all states of a trace, and ◊
+.CW p
+says that
+.CW p
+holds in at least one state of the trace.
+.LP
+To express our original example requirement: ``every system state
+in which property
+.CW P
+is true eventually leads to a system state in which property
+.CW Q
+is true,''
+we can write the LTL formula:
+.P1
+□ (P -> ◊ Q)
+.P2
+where the logical implication symbol
+.CW ->
+is defined in the usual way as
+.P1
+P => Q means !P || Q
+.P2
+.SH
+Mapping LTL Formulae onto Never Claims
+.LP
+\*P does not include syntax for specifying LTL formulae
+directly, but it relies on the fact that every such
+formula can be translated into a special type of
+automaton, known as a Büchi automaton.
+In the syntax of \*P this automaton is called a
+.CW never
+claim.
+If you don't care too much about the details of
+.CW never
+claims, you can skip the remainder of this section and
+simple remember that \*V can convert any LTL formula
+automatically into the proper never claim syntax with
+the command:
+.P1
+spin -f "...formula..."
+.P2
+Here are the details.
+The syntax of a never claim is:
+.P1
+never {
+ \&...
+}
+.P2
+where the dots can contain any \*P fragment, including
+arbitrary repetition, selection, unless constructs,
+jumps, etc.
+.LP
+There is an important difference in semantics between a
+.CW proctype
+declaration and a
+.CW never
+claim.
+Every statement inside a
+.CW never
+claim is interpreted as a proposition, i.e., a condition.
+A
+.CW never
+claim should therefore only contain expressions and never
+statements that can have side-effects (assignments, sends or
+receives, run-statements, etc.)
+.LP
+.CW Never
+claims are used to express behaviors that are considered
+undesirable or illegal.
+We say that a
+.CW never
+claim is `matched' if the undesirable behavior can be realized,
+contrary to the claim, and thus the correctness requirement violated.
+The claims are evaluated over system executions, that is, the
+propositions that are listed in the claim are evaluated over the
+traces from the remainder of the system.
+The claim, therefore, should not alter that behavior: it merely
+monitors it.
+Every time that the system reaches a new state, by asynchronously
+executing statements from the model, the claim will evaluate the
+appropriate propositions to determine if a counter-example can
+be constructed to the implicit LTL formula that is specified.
+.LP
+Since LTL formulae are only defined for infinite executions,
+the behavior of a
+.CW never
+claim can only be matched by an infinite system execution.
+This by itself would restrict us to the use of progress labels
+and accept labels as the only means we have discussed so far
+for expressing properties of infinite behaviors.
+To conform to standard omega automata theory, the behaviors of
+.CW never
+claims are expressed exclusively with
+.CW accept
+labels (never with
+.CW progress
+labels).
+To match a claim, therefore, an infinite sequence of true propositions
+must exist, at least one of which is labeled with an
+.CW accept
+label (inside the never claim).
+.LP
+Since \*P models can also express terminating system behaviors,
+we have to define the semantics of the
+.CW never
+claims also for those behaviors.
+To facilitate this, it is defined that a
+.CW never
+claim can also be matched when it reaches its closing curly brace
+(i.e., when it appears to terminate).
+This semantics is based on what is usually referred to as a `stuttering
+semantics.'
+With stuttering semantics, any terminating execution can be extended
+into an equivalent infinite execution (for the purposes of evaluating
+LTL properties) by repeating (stuttering) the final state infinitely often.
+As a syntactical convenience, the final state of a
+.CW never
+claim is defined to be accepting, i.e., it could be replaced with
+the explicit repetition construct:
+.P1
+accept: do :: skip od
+.P2
+Every process behavior, similarly, is (for the purposes of evaluating the
+.CW never
+claims) thought to be extended with a dummy self-loop on all final states:
+.P1
+ do :: skip od
+.P2
+(Note the
+.CW accept
+labels only occur in the
+.CW never
+claim, not in the system.)
+.SH
+The Semantics of a Never Claim
+.LP
+.CW Never
+claims are probably the hardest part of the language to understand,
+so it is worth spending a few extra words on them.
+On an initial reading, feel free to skip the remainder of this
+section.
+.LP
+The difference between a
+.CW never
+claim and the remainder of a \*P system can be explained
+as follows.
+A \*P model defines an asynchronous interleaving product of the
+behaviors of individual processes.
+Given an arbitrary system state, its successor states are
+conceptually obtained in two steps.
+In a first step, all the executable statements in the
+individual processes are identified.
+In a second step, each one of these statements is executed,
+each one producing one potential successor for the current state.
+The complete system behavior is thus defined recursively and
+represents all possible interleavings of the individual process behaviors.
+It is this asynchronous product machine that we call the `global
+system behavior'.
+.LP
+The addition of a
+.CW never
+claim defines a \f2synchronous\f1 product of the global system behavior
+with the behavior expressed in the claim.
+This synchronous product can be thought of as the construction of a
+new global state machine, in which every state is defined as a pair
+.CW (s,n)
+with
+.CW s
+a state from the global system (the asynchronous product of processes), and
+.CW n
+a state from the claim.
+Every transition in the new global machine is similarly defined by a pair
+of transitions, with the first element a statement from the system, and the
+second a proposition from the claim.
+In other words, every transition in this final synchronous product is
+defined as a joint transition of the system and the claim.
+Of course, that transition can only occur if the proposition from the
+second half of the transition pair evaluates to true in the current state
+of the system (the first half of the state pair).
+.SH
+Examples
+.LP
+To manually translate an LTL formula into a
+.CW never
+claim (e.g. foregoing the builtin translation that \*V
+offers), we must carefully consider whether the
+formula expresses a positive or a negative property.
+A positive property expresses a good behavior that we
+would like our system to have.
+A negative property expresses a bad behavior that we
+claim the system does not have.
+A
+.CW never
+claim can express only negative claims, not positive ones.
+Fortunately, the two are exchangeable: if we want to express
+that a good behavior is unavoidable, we can formalize all
+ways in which the good behavior could be violated, and express
+that in the
+.CW never
+claim.
+.LP
+Suppose that the LTL formula ◊□
+.CW p ,
+with
+.CW p
+a \*P expression, expresses a negative claim
+(i.e., it is considered a correctness violation if
+there exists any execution sequence in which
+.CW p
+can eventually remain true infinitely long).
+This can be written in a
+.CW never
+claim as:
+.P1
+never { /* <>[]p */
+ do
+ :: skip /* after an arbitrarily long prefix */
+ :: p -> break /* p becomes true */
+ od;
+accept: do
+ :: p /* and remains true forever after */
+ od
+}
+.P2
+Note that in this case the claim does not terminate, and
+also does not necessarily match all system behaviors.
+It is sufficient if it precisely captures all violations
+of our correctness requirement, and no more.
+.LP
+If the LTL formula expressed a positive property, we first
+have to invert it to the corresponding negative property
+.CW ◊!p
+and translate that into a
+.CW never
+claim.
+The requirement now says that it is a violation if
+.CW p
+does not hold infinitely long.
+.P1
+never { /* <>!p*/
+ do
+ :: skip
+ :: !p -> break
+ od
+}
+.P2
+We have used the implicit match of a claim upon reaching the
+closing terminating brace.
+Since the first violation of the property suffices to disprove
+it, we could also have written:
+.P1
+never { /* <>!p*/
+ do
+ :: p
+ :: !p -> break
+ od
+}
+.P2
+or, if we abandon the connection with LTL for a moment,
+even more tersely as:
+.P1
+never { do :: assert(p) od }
+.P2
+Suppose we wish to express that it is a violation of our
+correctness requirements if there exists any execution in
+the system where
+.CW "□ (p -> ◊ q)"
+is violated (i.e., the negation of this formula is satisfied).
+The following
+.CW never
+claim expresses that property:
+.P1
+never {
+ do
+ :: skip
+ :: p && !q -> break
+ od;
+accept:
+ do
+ :: !q
+ od
+}
+.P2
+Note that using
+.CW "(!p || q)"
+instead of
+.CW skip
+in the first repetition construct would imply a check for just
+the first occurrence of proposition
+.CW p
+becoming true in the execution sequence, while
+.CW q
+is false.
+The above formalization checks for all occurrences, anywhere in a trace.
+.LP
+Finally, consider a formalization of the LTL property
+.CW "□ (p -> (q U r))" .
+The corresponding claim is:
+.P1
+never {
+ do
+ :: skip /* to match any occurrence */
+ :: p && q && !r -> break
+ :: p && !q && !r -> goto error
+ od;
+ do
+ :: q && !r
+ :: !q && !r -> break
+ od;
+error: skip
+}
+.P2
+Note again the use of
+.CW skip
+instead of
+.CW "(!p || r)"
+to avoid matching just the first occurrence of
+.CW "(p && !r)"
+in a trace.
+.NH 2
+Predefined Variables and Functions
+.LP
+The following predefined variables and functions
+can be especially useful in
+.CW never
+claims.
+.LP
+The predefined variables are:
+.CW _pid
+and
+.CW _last .
+.LP
+.CW _pid
+is a predefined local variable in each process
+that holds the unique instantiation number for
+that process.
+It is always a non-negative number.
+.LP
+.CW _last
+is a predefined global variable that always holds the
+instantiation number of the process that performed the last
+step in the current execution sequence.
+Its value is not part of the system state unless it is
+explicitly used in a specification.
+.P1
+never {
+ /* it is not possible for the process with pid=1
+ * to execute precisely every other step forever
+ */
+accept:
+ do
+ :: _last != 1 -> _last == 1
+ od
+}
+.P2
+The initial value of
+.CW _last
+is zero.
+.LP
+Three predefined functions are specifically intended to be used in
+.CW never
+claims, and may not be used elsewhere in a model:
+.CW pc_value(pid) ,
+.CW enabled(pid) ,
+.CW procname[pid]@label .
+.LP
+The function
+.CW pc_value(pid)
+returns the current control state
+of the process with instantiation number
+.CW pid ,
+or zero if no such process exists.
+.LP
+Example:
+.P1
+never {
+ /* Whimsical use: claim that it is impossible
+ * for process 1 to remain in the same control
+ * state as process 2, or one with smaller value.
+ */
+accept: do
+ :: pc_value(1) <= pc_value(2)
+ od
+}
+.P2
+The function
+.CW enabled(pid)
+tells whether the process with instantiation number
+.CW pid
+has an executable statement that it can execute next.
+.LP
+Example:
+.P1
+never {
+ /* it is not possible for the process with pid=1
+ * to remain enabled without ever executing
+ */
+accept:
+ do
+ :: _last != 1 && enabled(1)
+ od
+}
+.P2
+The last function
+.CW procname[pid]@label
+tells whether the process with instantiation number
+.CW pid
+is currently in the state labeled with
+.CW label
+in
+.CW "proctype procname" .
+It is an error if the process referred to is not an instantiation
+of that proctype.
+.NH 1
+Verifications with \*V
+.LP
+The easiest way to use \*V is probably on a Windows terminal
+with the Tcl/Tk implementation of \s-1XSPIN\s0.
+All functionality of \*V, however, is accessible from
+any plain ASCII terminal, and there is something to be
+said for directly interacting with the tool itself.
+.LP
+The description in this paper gives a short walk-through of
+a common mode of operation in using the verifier.
+A more tutorial style description of the verification
+process can be found in [Ho93].
+More detail on the verification of large systems with the
+help of \*V's supertrace (bitstate) verification algorithm
+can be found in [Ho95].
+.IP
+.RS
+.br
+\(bu
+Random and interactive simulations (section 2.1),
+.br
+\(bu
+Generating a verifier (section 2.2),
+.br
+\(bu
+Compilation for different types of searches (section 2.3),
+.br
+\(bu
+Performing the verification (section 2.4),
+.br
+\(bu
+Inspecting error traces produced by the verifier (section 2.5),
+.br
+\(bu
+Exploiting partial order reductions (section 2.6).
+.RE
+.LP
+.NH 2
+Random and Interactive Simulations
+.LP
+Given a model in \*P, say stored in a file called
+.CW spec ,
+the easiest mode of operation is to perform a random simulation.
+For instance,
+.P1
+spin -p spec
+.P2
+tells \*V to perform a random simulation, while printing the
+process moves selected for execution at each step (by default
+nothing is printed, other than explicit
+.CW printf
+statements that appear in the model itself).
+A range of options exists to make the traces more verbose,
+e.g., by adding printouts of local variables (add option
+.CW -l ),
+global variables (add option
+.CW -g ),
+send statements (add option
+.CW -s ),
+or receive statements (add option
+.CW -r ).
+Use option
+.CW -n N
+(with N any number) to fix the seed on \*V's internal
+random number generator, and thus make the simulation runs
+reproducible.
+By default the current time is used to seed the random number
+generator.
+For instance:
+.P1
+spin -p -l -g -r -s -n1 spec
+.P2
+.LP
+If you don't like the system randomly resolving non-deterministic
+choices for you, you can select an interactive simulation:
+.P1
+spin -i -p spec
+.P2
+In this case you will be offered a menu with choices each time
+the execution could proceed in more than one way.
+.LP
+Simulations, of course, are intended primarily for the
+debugging of a model. They cannot prove anything about it.
+Assertions will be evaluated during simulation runs, and
+any violations that result will be reported, but none of
+the other correctness requirements can be checked in this way.
+.NH 2
+Generating the Verifier
+.LP
+A model-specific verifier is generated as follows:
+.P1
+spin -a spec
+.P2
+This generates a C program in a number of files (with names
+starting with
+.CW pan ).
+.NH 2
+Compiling the Verifier
+.LP
+At this point it is good to know the physical limitations of
+the computer system that you will run the verification on.
+If you know how much physical (not virtual) memory your system
+has, you can take advantage of that.
+Initially, you can simply compile the verifier for a straight
+exhaustive verification run (constituting the strongest type
+of proof if it can be completed).
+Compile as follows.
+.P1
+\*C -o pan pan.c # standard exhaustive search
+.P2
+If you know a memory bound that you want to restrict the run to
+(e.g., to avoid paging), find the nearest power of 2 (e.g., 23
+for the bound $2 sup 23# bytes) and compile as follows.
+.P1
+\*C '-DMEMCNT=23' -o pan pan.c
+.P2
+or equivalently in terms of MegaBytes:
+.P1
+\*C '-DMEMLIM=8' -o pan pan.c
+.P2
+If the verifier runs out of memory before completing its task,
+you can decide to increase the bound or to switch to a frugal
+supertrace verification. In the latter case, compile as follows.
+.P1
+\*C -DBITSTATE -o pan pan.c
+.P2
+.NH 2
+Performing the Verification
+.LP
+There are three specific decisions to make to
+perform verifications optimally: estimating the
+size of the reachable state space (section 2.4.1),
+estimating the maximum length of a unique execution
+sequence (2.4.2), and selecting the type of correctness
+property (2.4.3).
+No great harm is done if the estimates from the first two
+steps are off. The feedback from the verifier usually provides
+enough clues to determine quickly what the optimal settings
+for peak performance should be.
+.NH 3
+Reachable States
+.LP
+For a standard exhaustive run, you can override the default choice
+for the size for the hash table ($2 sup 18# slots) with option
+.CW -w .
+For instance,
+.P1
+pan -w23
+.P2
+selects $2 sup 23# slots.
+The hash table size should optimally be roughly equal to the number of
+reachable states you expect (within say a factor of two or three).
+Too large a number merely wastes memory, too low a number wastes
+CPU time, but neither can affect the correctness of the result.
+.sp
+For a supertrace run, the hash table \f2is\f1 the memory arena, and
+you can override the default of $2 sup 22# bits with any other number.
+Set it to the maximum size of physical memory you can grab without
+making the system page, again within a factor of say two or three.
+Use, for instance
+.CW -w23
+if you expect 8 million reachable states and have access to at least
+8 million ($2 sup 23#) bits of memory (i.e., $2 sup 20# or 1 Megabyte of RAM).
+.NH 3
+Search Depth
+.LP
+By default the analyzers have a search depth restriction of 10,000 steps.
+If this isn't enough, the search will truncate at 9,999 steps (watch for
+it in the printout).
+Define a different search depth with the -m flag.
+.P1
+pan -m100000
+.P2
+If you exceed also this limit, it is probably good to take some
+time to consider if the model you have specified is indeed finite.
+Check, for instance, if no unbounded number of processes is created.
+If satisfied that the model is finite, increase the search depth at
+least as far as is required to avoid truncation completely.
+.LP
+If you find a particularly nasty error that takes a large number of steps
+to hit, you may also set lower search depths to find the shortest variant
+of an error sequence.
+.P1
+pan -m40
+.P2
+Go up or down by powers of two until you find the place where the
+error first appears or disappears and then home in on the first
+depth where the error becomes apparent, and use the error trail of
+that verification run for guided simulation.
+.sp
+Note that if a run with a given search depth fails to find
+an error, this does not necessarily mean that no violation of a
+correctness requirement is possible within that number of steps.
+The verifier performs its search for errors by using a standard
+depth-first graph search. If the search is truncated at N steps,
+and a state at level N-1 happens to be reachable also within fewer
+steps from the initial state, the second time it is reached it
+will not be explored again, and thus neither will its successors.
+Those successors may contain errors states that are reachable within
+N steps from the initial state.
+Normally, the verification should be run in such a way that no
+execution paths can be truncated, but to force the complete exploration
+of also truncated searches one can override the defaults with a compile-time
+flag
+.CW -DREACH .
+When the verifier is compiled with that additional directive, the depth at
+which each state is visited is remembered, and a state is now considered
+unvisited if it is revisited via a shorter path later in the search.
+(This option cannot be used with a supertrace search.)
+.NH 3
+Liveness or Safety Verification
+.LP
+For the last, and perhaps the most critical, runtime decision:
+it must be decided if the system is to be checked for safety
+violations or for liveness violations.
+.P1
+pan -l # search for non-progress cycles
+pan -a # search for acceptance cycles
+.P2
+(In the first case, though, you must compile pan.c with -DNP as an
+additional directive. If you forget, the executable will remind you.)
+If you don't use either of the above two options, the default types of
+correctness properties are checked (assertion violations,
+completeness, race conditions, etc.).
+Note that the use of a
+.CW never
+claim that contains
+.CW accept
+labels requires the use of the
+.CW -a
+flag for complete verification.
+.LP
+Adding option
+.CW -f
+restricts the search for liveness properties further under
+a standard \f2weak fairness\f1 constraint:
+.P1
+pan -f -l # search for weakly fair non-progress cycles
+pan -f -a # search for weakly fair acceptance cycles
+.P2
+With this constraint, each process is required to appear
+infinitely often in the infinite trace that constitutes
+the violation of a liveness property (e.g., a non-progress cycle
+or an acceptance cycle), unless it is permanently blocked
+(i.e., has no executable statements after a certain point in
+the trace is reached).
+Adding the fairness constraint increases the time complexity
+of the verification by a factor that is linear in the number
+of active processes.
+.LP
+By default, the verifier will report on unreachable code in
+the model only when a verification run is successfully
+completed.
+This default behavior can be turned off with the runtime option
+.CW -n ,
+as in:
+.P1
+pan -n -f -a
+.P2
+(The order in which the options such as these are listed is
+always irrelevant.)
+A brief explanation of these and other runtime options can
+be determined by typing:
+.P1
+pan --
+.P2
+.NH 2
+Inspecting Error Traces
+.LP
+If the verification run reports an error,
+any error, \*V dumps an error trail into a file named
+.CW spec.trail ,
+where
+.CW spec
+is the name of your original \*P file.
+To inspect the trail, and determine the cause of the error,
+you must use the guided simulation option.
+For instance:
+.P1
+spin -t -c spec
+.P2
+gives you a summary of message exchanges in the trail, or
+.P1
+spin -t -p spec
+.P2
+gives a printout of every single step executed.
+Add as many extra or different options as you need to pin down the error:
+.P1
+spin -t -r -s -l -g spec
+.P2
+Make sure the file
+.CW spec
+didn't change since you generated the analyzer from it.
+.sp
+If you find non-progress cycles, add or delete progress labels
+and repeat the verification until you are content that you have found what
+you were looking for.
+.sp
+If you are not interested in the first error reported,
+use pan option
+.CW -c
+to report on specific others:
+.P1
+pan -c3
+.P2
+ignores the first two errors and reports on the third one that
+is discovered.
+If you just want to count all errors and not see them, use
+.P1
+pan -c0
+.P2
+.SH
+State Assignments
+.LP
+Internally, the verifiers produced by \*V deal with a formalization of
+a \*P model in terms of extended finite state machines.
+\*V therefore assigns state numbers to all statements in the model.
+The state numbers are listed in all the relevant output to make it
+completely unambiguous (source line references unfortunately do not
+have that property).
+To confirm the precise state assignments, there is a runtime option
+to the analyzer generated:
+.P1
+pan -d # print state machines
+.P2
+which will print out a table with all state assignments for each
+.CW proctype
+in the model.
+.NH 2
+Exploiting Partial Order Reductions
+.LP
+The search algorithm used by \*V is optimized
+according to the rules of a partial order theory explained in [HoPe94].
+The effect of the reduction, however, can be increased considerably if the verifier
+has extra information about the access of processes to global
+message channels.
+For this purpose, there are two keywords in the language that
+allow one to assert that specific channels are used exclusively
+by specific processes.
+For example, the assertions
+.P1
+xr q1;
+xs q2;
+.P2
+claim that the process that executes them is the \f2only\f1 process
+that will receive messages from channel
+.CW q1 ,
+and the \f2only\f1 process that will send messages to channel
+.CW q2 .
+.LP
+If an exclusive usage assertion turns out to be invalid, the
+verifier will be able to detect this, and report it as a violation
+of an implicit correctness requirement.
+.LP
+Every read or write access to a message channel can introduce
+new dependencies that may diminish the maximum effect of the
+partial order reduction strategies.
+If, for instance, a process uses the
+.CW len
+function to check the number of messages stored in a channel,
+this counts as a read access, which can in some cases invalidate
+an exclusive access pattern that might otherwise exist.
+There are two special functions that can be used to poll the
+size of a channel in a safe way that is compatible with the
+reduction strategy.
+.LP
+The expression
+.CW nfull(qname)
+returns true if channel
+.CW qname
+is not full, and
+.CW nempty(qname)
+returns true if channel
+.CW qname
+contains at least one message.
+Note that the parser will not recognize the free form expressions
+.CW !full(qname)
+and
+.CW !empty(qname)
+as equally safe, and it will forbid constructions such as
+.CW !nfull(qname)
+or
+.CW !nempty(qname) .
+More detail on this aspect of the reduction algorithms can be
+found in [HoPe94].
+.SH
+Keywords
+.LP
+For reference, the following table contains all the keywords,
+predefined functions, predefined variables, and
+special label-prefixes of the language \*P,
+and refers to the section of this paper in
+which they were discussed.
+.KS
+.TS
+center;
+l l l l.
+_last (1.4) _pid (1.1.1) accept (1.3.2) active (1.1.1)
+assert (1.3.1) atomic (1.2.1) bit (1.1.2) bool (1.1.2)
+break (1.2.4) byte (1.1.2) chan (1.1.3) d_step (1.2.2)
+do (1.2.4) else (1.2.4) empty (1.1.3) enabled (1.4)
+end (1.3.2) fi (1.2.3) full (1.1.3) goto (1.2.2)
+hidden (not discussed) if (1.2.3) init (1.1.1) int (1.1.2)
+len (1.1.3) mtype (1.1.3) nempty (2.6) never (1.3.3)
+nfull (2.6) od (1.2.4) of (1.1.3) pc_value (1.4)
+printf (1.1.1) proctype (1.1.1) progress (1.3.2) run (1.1.1)
+short (1.1.2) skip (1.2) timeout (1.2.4) typedef (1.1.2)
+unless (1.2.5) xr (2.6) xs (2.6)
+.TE
+.KE
+.SH
+References
+.LP
+[Ho91]
+G.J. Holzmann,
+.I
+Design and Validation of Computer Protocols,
+.R
+Prentice Hall, 1991.
+.LP
+[Ho93]
+G.J. Holzmann, ``Tutorial: Design and Validation of Protocols,''
+.I
+Computer Networks and ISDN Systems,
+.R
+1993, Vol. 25, No. 9, pp. 981-1017.
+.LP
+[HoPe94]
+G.J. Holzmann and D.A. Peled, ``An improvement in
+formal verification,''
+.I
+Proc. 7th Int. Conf. on Formal Description Techniques,
+.R
+FORTE94, Berne, Switzerland. October 1994.
+.LP
+[Ho95]
+G.J. Holzmann, ``An Analysis of Bitstate Hashing,''
+technical report 2/95, available from author.
+.LP
+[HS99]
+G.J. Holzmann, ``Software model checking: extracting
+verification models from source code,''
+.I
+Proc. Formal Methods in Software Engineering and Distributed
+Systems,
+.R
+PSTV/FORTE99, Beijng, China, Oct. 1999, Kluwer,pp. 481-497.