learning_tla/lamport_video/docs/lecture09-2.md
2025-02-20 16:06:12 +01:00

98 lines
No EOL
4.8 KiB
Markdown

# 9.2 Alternating Bit protocol: the protocol
A and B communicate over two lossy channels AtoB and BtoA.
A sends a value. Since the channels are lossy, A keeps sending it.
Meanwhile, B acknowledges the value it receives by sending its bit (0 or 1).
Similarly, since the channels are lossy, it keeps sending it too.
When B receives a message, it knows this is a new value because the bit is different
from its current one, and starts sending the new one. When A eventually receives
a bit that differs from the one it holds, it knows that the message was successfully
transmitted. It can then go one to transmit the next message.
## Checking safety
Because the protocol is not terminating, the behaviors are infinitely long,
so TLC will fail to check them all in finite time.
To work around this we can add a state constraint like `Len(AtoB) <= 3 /\ Len(BtoA) <= 3`
However, although the toolbox exposes this, it is not directly supported by TLC:
the toolbox does it by creating its own on-the-fly spec `MC.tla`, in which it
adds a new predicate. Without the toolbox, we have to add it in the spec, like
`CheckLimits == Len(AtoB) <= 4 /\ Len(BtoA) <= 4`,
then the CFG file can contain `CONSTRAINT CheckLimits`.
This will cause TLC to only check states matching that state constraint.
We can check that AB implements its high-level spec ABSpec
so formula `Spec` in module AB should imply formula `Spec` in module ABSpec:
this should be a theorem of module AB.
We cannot use `INSTANCE ABSpec` in module AB because it imports definitions
of `Spec` and others, which already exist in module AB, so we shall use a namespace
to avoid name clashes: `ABS == INSTANCE ABSpec`, so now we can access
the definition os `Spec` in module ABSpec as `ABS!Spec`, avoiding name clashes.
This enables us to write theorem: `THEOREM Spec => ABS!Spec`.
## Checking liveness
We want fairness properties implying that messages keep getting sent and received,
meaning `THEOREM FairSpec => ABS!FairSpec`.
We cannot just require weak fairness of the Next action: for example, it would
allow a behavior in which B keeps sending the same bit forever, and nothing else:
since this is not stuttering, it would be a valid behavior.
Next is defined as the union of ASnd, ARcv, BSnd, BRcv, and LoseMsg, so we need
fairness requirements on each send and receive action. We do not need fairness
on LoseMsg, because we do not want to require that messages be lost.
If we require only weak fairness from ASnd and BSnd and all messages are lost,
weak fairness is satisfied, but the protocol is not live. Similarly, ARcv and BRcv
alternate between being enabled when the other sends, and being disabled when
these messages are lost. So weak fairness is true for them too because ARcv and BRcv
are never continuously enabled.
We need to require strong fairness for the receive actions, so that if a message
is sent, it will eventually be received.
So the behavior satisfies
```tla+ code fragment
FairSpec == Spec /\ WF_vars(ARcv) /\ WF_vars(BRcv) /\
WF_vars(ASnd) /\ WF_vars(BSnd)
```
But it does not satisfy `ABS!FairSpec`. We need to add strong fairness requirement
on some messages being eventually received. Since ASnd and BSnd are always enabled,
with no requirement, they does not need a strong fairness requirement.
## What good is liveness?
- Is it useful to know if something eventually happens, if it can happen after the end of the universe?
- How can we ensure strong fairness in practice for the ARcv and BRcv actions?
- Or even know that it is not satisfied ?
Specifications are abstractions. As such, they are a compromise beween our desires
for accuracy and reasonable simplicity. Specifying transmission delays, success rates, etc,
would complexify the spec a lot, so it is simpler to just require that it eventually be received:
if it fails being eventually received, it will not be received in any given finite time anyway.
For system without hard real-time requirements (rockets, pacemakers, etc), liveness
checking is a useful enough way to find errors that prevent things from happening.
Many systems use timeouts only to ensure that something must happen eventually.
Correctness of such a system does not depend on how long it takes for the timeout to occur:
that only influences performance, not correctness.
Specifying such systems can describe the timeouts as actions with no time constraints,
only weak fairness conditions. This is true for most systems with no bounds on how
long it can take an enabled operation (e.g. receiving a message) to occur.
Liveness is subtle and hard to get right, but safety is most often by far the
most important part of specs, and liveness is often added mostly to catch errors
in the safety part.
Fairness conditions do NOT imply the _eventually_ or _leads to_ properties
one could expect, possibly because the safety part would not allow those.