6.6 KiB
09: Implementation - how it works - two-bit theorem
All specs until now focused on specifying what a system MAY do. This lecture will focus on what a system MUST do.
Finite sequences aka lists
Finite sequence is another name for tuple.
<< -3, "xyz", {0,2}>> is a tuple (or sequence) of length 3.
<< -3, "xyz", {0,2}>>[1] = -3<< -3, "xyz", {0,2}>>[2] = "xyz"<< -3, "xyz", {0,2}>>[3] = {0,2}
They can be defined in function notation, e.g. the squares sequence for 1..N:
[i \in 1..N |-> i^2]
This shows how brackets, seen in programming as dereferencing, are actually applications of the sequence function to a value in its domain.
The Sequences module defines operators for sequences:
Tail(<<s_1, ..., s_n>>) equals<<s_2, ..., s_n>>'Head(seq) == seq[1]\oconcatenates sequencesIF seq # <<>> THEN seq = <<Head(seq)>> \o Tail(seq)
Append(seq, e) == seq \o <<e>>Len(seq)equals the length of the sequenceDOMAIN seq = 1..Len(seq)1..0 = {} = DOMAIN <<>>
Seq(S)is the set of ALL sequences with elements inS.- That means it is infinite:
Set({3}) = {<<>>, <<3>>, <<3,3>>, <<3,3,3>>, ...}
- That means it is infinite:
Remove(i, seq)is defined only in this lecture, not the Sequences module. It removes the i^th element from seq.Len(Remove(i, seq)) = Len(seq) - 1Remove(i, seq) == [j \in 1..(Len(seq) - 1) |-> IF j < i THEN seq[j] ELSE seq[j+1] ]
Evaluating constant expressions
To evaluate any constant expressions in a model:
- in the toolbox, check "no behavior" and enter the expression under "Evaluate constant expression", then input expression
- with TLC, disable SPECIFICATION, INIT, NEXT in the CFG file, and add a line like:
ASSUME PrintT(<<"Eval", Remove(3, <<1, 2, 3, 4>>)>>)in the spec. Running TLC will include a line with<<"Eval", <<1, 2, 4>>>>in its results. Commandtlceval.gocan help
$ tlc -tool l09_remove.tla | go run bin/tlceval.go
<<1, 2, 3>>
Cartesian product
\A S, T: S \X T = {<<a, b>>: a \in S, b \in T}
tlc -tool l09_cartesian.tla | go run bin/tlceval.go
{<<1, "a">>, <<1, "b">>, <<2, "a">>, <<2, "b">>, <<3, "a">>, <<3, "b">>}
Alternating Bit (AB) protocol
Intro
Sender A sends a sequence of data items to receiver B. Let them be strings for now.
If we sent the same string repeatedly, we cannot know what was sent if we only keep the state as the current string.
Instead, let us keep it as a sequence <<theString,bit>>, where bit \in {0,1}
This allows detecting repeated values, without having to rely on a clock.
Spec
See ABSpec.
Type correctness, checked by the TypeOK invariant, doesn't mean that the spec is correct.
To find errors, we need to check that formulas which should be invariant are. In our case, if the bits are equal, then the whole values are too.
At this point, formula Spec only asserts what MAY happen.
Safety and liveness
- A safety formula is a temporal formula which only asserts what MAY happen
- Any behavior that violates it does so at some point
- Nothing past that point makes any difference (so the model check can stop)
- A typical spec like
Init /\ [][Next]_varscan only be violated if the initial step does not satisfyIniton the first state of the behavior, or if a step does not satisfy[Next]_vars: the step neither satisfies Next nor leaves vars unchanged - Once that has happend, nothing later in the formula can cause it to become true again
- A liveness formula is a temporal formula which only asserts what MUST happen
- A behavior can NOT violate it at any point: the rest of the behaviour can always make it true
- Example:
x = 5on some state of the behavior- At any point, it is always possible for a later state to satisfy it
- A behavior is any infinite sequence of states
- This is asserted by
<>(x = 5)
- The only liveness property sequential programs must satisfy is termination,
noted
<>Terminated - Concurrent systems can have a wide variety of liveness requirements
- Example for ABSpec: if
AVar = <"hi", 0>in some state, thenBVAr = <"hi", 0> in the same state or a later one(AVar = <<"hi", 0>>) ~> (BVar = <<"hi", 0>>)`
More generally we would like the AB protocol to satisfy: "Any value being sent by A is eventually received by B"
\A v \in Data \X {0,1}: (AVar = v) ~> (BVar = v)
Check: <>P is equivalent to ~[]~P
Weak fairness
Definition
An action is enabled in state s iff there is a state t such
as A is true on step s -> t, usually said "s -> t is an A step".
A conjunct (e.g. AVar = BVar) with no primes is an assertion about the first state.
A == /\ AVar = BVar \* Can be taken when values are equal
/\ \E d \in Data: AVar' = <<d, 1 - AVar[2]>> \* Switch the second element
/\ BVar' = BVar
A is enabled only if AVar = BVar AND Data # {}, otherwise there
exists no element in Data for the \E quantifier.
Weak fairness of action A asserts of a behaviour:
- if A ever remains continuously enabled
- then an A step must eventually occur
Or equivalently: A cannot remain enabled forever without another A step occurring.
Weak fairness of A is written as temporal formula WF_vars(A), where vars is
the tuple of all the spec's variables.
WF_vars(A) is a liveness property, because it can always be made true by an A
step or a state in which A is not enabled.
Adding fairness to a spec
A spec with liveness is written: Init /\ [][Next]_vars /\ Fairness where:
- Fairness is a conjunction of
WF_vars(A)andSF_vars(a)formulas- where each A is a subaction of
Next(every A step is a Next step)
- where each A is a subaction of
In AB, this is FairSpec == Spec /\ WF_vars(Next): it asserts that a behavior
keeps taking Next steps as long as Next is enabled, meaning as long as the
system is not in a deadlocked or terminated state: the safety part of this
spec ensures such a state cannot be reached, meaning it wll keep sending and
receiving values forever: it doesn't not deadlock or terminate.
For liveness checking, the model MUST NOT have any SYMMETRY set.
Another possible high-level spec of AB would be FairSpec == Spec /\ WF_vars(B)
- it requires every sent value to be received
- but it allows the sender to stop sending
TODO: explain why these are equivalent:
Init /\ [][Next]_vars /\ WF_vars(Next)
Init /\ [][Next]_vars /\ WF_vars(A) /\ WF_vars(B)
Confirm with TLC
_vars
We add this non-stuttering step because it makes no sense to require a stuttering step to occur, since there is no way to tell that it did (no clock).