4.8 KiB
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
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.