78 lines
3.2 KiB
Markdown
78 lines
3.2 KiB
Markdown
# 8.2 Implementation - how it works
|
|
## Specs implementing other specs
|
|
|
|
For `TCommit` the specification is `TCSpec == TCInit /\ [][TCNext]_<<rmState>>`,
|
|
which we can simplify as `TCSpec == TCInit /\ [][TCNext]_rmState`
|
|
|
|
Module `TwoPhase` contains
|
|
|
|
- `INSTANCE TCommit`, importing that module, including the definition of `TCSpec`.
|
|
- `THEOREM TPSpec => TCSpec`, which asserts that for every behavior,
|
|
if the behavior satisfies `TPSpec` then it also satisfies `TCSpec`.
|
|
- This is what _`TPSpec` implements `TCSpec`_ means
|
|
|
|
We can check it by adding `TCSpec` as a property to check on TwoPhase.
|
|
|
|
How can this theorem make sense: `THEOREM TPSpec => TCSpec` ?
|
|
- `TPSpec` is an assertion about behaviors whose states assign values to four variables: `rmState`, `tmState`, `tmPrepared` and `msgs`.
|
|
- `TCSpec` is an assertion about behaviors whose states assign values to one variable:`rmState`.
|
|
|
|
A state is an assignment of values to ALL variables, not just those in the module
|
|
defining them.
|
|
|
|
So when evaluating `TCSpec` during the theorem evaluation, TLC will verify that `rmState` is
|
|
unchanged, but not verify the other variables.
|
|
|
|
This seems weird when thinking of specs as programs. But they are not.
|
|
|
|
**A specification does NOT describe the correct behavior of a system.**
|
|
|
|
It describes a universe in which the system AND its environment are behaving correctly.
|
|
|
|
## Stuttering
|
|
|
|
`THEOREM TPSpec => TCSpec` makes sense because both formulas are assertions about
|
|
the same kind of behavior.
|
|
|
|
- `TPSpec == TPInit /\ [][TPNext]_<<...>>`
|
|
- `TPNext` includes `TMAbort` specs
|
|
- `TMAbort == tmState = "init" /\ tmState' = "done" /\ msgs' = msgs \cup {[type |-> "Abort"]} /\ UNCHANGED <<rmState, tmPrepared>>`
|
|
- meaning that these only allows states that leave `rmState` unchanged
|
|
- `TCSpec == TCInit /\ [][TCNext]_rmState`
|
|
- all `TCNext` steps change `rmState`
|
|
- So `TMAbort` cannot be a `TCNext` step since it changes `rmState`
|
|
|
|
If steps leaving `rmState` unchanged were not allowed by `TCSpec`, the theorem would not be true,
|
|
because `TMAbort` only contains states that leave it unchanged.
|
|
|
|
This is why we always add the `[ ]_<<vars>>` in the spec formula: to allow all
|
|
steps outside those we want to verify, that do not change what the formula is about.
|
|
These are called _stuttering_ steps.
|
|
|
|
**ALL TLA+ specs allow stuttering steps.** If they didn't, any spec would allow
|
|
the value of `whateverUnrelatedVariableYouWant` to change only when the protocol
|
|
took a step. Which is a major case of aggrandizing.
|
|
|
|
But fundamentally the important part is that implementation IS implication.
|
|
|
|
Math simplicity is not an end in itself, but is a sign that we are doing things right.
|
|
|
|
## Termination and stopping
|
|
|
|
A terminating or _deadlock_ execution is represented by a behavior that end
|
|
in an infinite sequence of stuttering steps: the universe keeps going after
|
|
the system terminates.
|
|
|
|
All the specs we've seen so far allow a system to stop at any time,
|
|
by taking infinitely many stuttering steps.
|
|
|
|
Specs specify what a system MAY do, not what it MUST do. They allow it to do nothing.
|
|
|
|
We add temporal requirements by conjoining (/\-ing) a temporal formula to the specification,
|
|
but that is not always necessary: the examples like two-phase commit or Paxos only use "may".
|
|
|
|
|
|
|
|
|
|
|
|
|