178 lines
6.6 KiB
Markdown
178 lines
6.6 KiB
Markdown
# 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]`
|
|
- `\o` concatenates sequences
|
|
- `IF seq # <<>> THEN seq = <<Head(seq)>> \o Tail(seq)`
|
|
- `Append(seq, e) == seq \o <<e>>`
|
|
- `Len(seq)` equals the length of the sequence
|
|
- `DOMAIN seq = 1..Len(seq)`
|
|
- `1..0 = {} = DOMAIN <<>>`
|
|
- `Seq(S)` is the set of ALL sequences with elements in `S`.
|
|
- That means it is infinite: `Set({3}) = {<<>>, <<3>>, <<3,3>>, <<3,3,3>>, ...}`
|
|
- `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) - 1`
|
|
- `Remove(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.
|
|
Command `tlceval.go` can 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](../specs/ABSpec.tla).
|
|
|
|
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]_vars` can only be violated if the initial
|
|
step does not satisfy `Init` on 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 = 5` on 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, then `BVAr = <"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"
|
|
|
|
```tla+ code fragment
|
|
\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.
|
|
|
|
```tla+ code fragment
|
|
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)` and `SF_vars(a)` formulas
|
|
- where each A is a subaction of `Next` (every A step is a Next step)
|
|
|
|
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:
|
|
```tla+ code fragment
|
|
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).
|
|
|