learning_tla/lamport_video/docs/lecture08-2.md
2025-02-17 18:08:25 +01:00

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".