never { do :: assert(p) od }
An easy alternative is also to generate the never claim from the
negated LTL property ![]p. The resulting never claim
would then be equivalent to the following:
never { do :: !p -> break :: true od }
This time the violation is caught when the end of the claim
is reached, so instead of an assertion violation the error will
now be reported as a match of the claim, but the result is the
same.
If a never claim is already being used to check another property, which cannot easily be extended with the invariant check, an easy alternative is to add a special purpose invariant checking process to the model, as follows.
active proctype invariant() { atomic { !p -> assert(p) } }
Note that, similar to the pattern used in the second never claim,
this process cannot terminate unless the assertion can indeed fail.
The best method would be to avoid the addition of either a never claim or a user-defined process but to simply extend the existing model with process level assertions that are placed immediately following only those statements in the model that could in principle cause a violation of the invariant (e.g., after every statement that modifies a variable that is referenced in the invariant property).
active proctype invariant() { do :: assert(p) od }
The disadvantage of this method is that the presence of
an invariant process of this type will have the effect that the
system as a whole can no longer deadlock: there is always one transition
left to execute. This also means that the special predefined boolean
variable timeout can no longer become true at any system state.
We could avoid the latter problem by removing the do-loop and use the following simple form:
active proctype invariant() { assert(p) }
This works correctly, but it now causes the verifier to explore two
transitions for every reachable state, instead of one: the evaluation
of the presumably valid assertion and the subsequent death of the process,
which can now terminate.
This version, therefore, introduces more overhead than is necessary.
There is one trick one can apply to avoid the death of the process, and
with that the creation of additional system states, which is to create
the invariant process as the first running process in the system.
Because processes can only die in last-in-first out order, this would
secure that the terminated invariant process cannot die and disappear
until all other processes have died first.
| Go to Spin Home Page | July 17, 2004 |