18. Transition Systems
Mar 15, 2021
Transition systems
Parallel computing: from RAM model → PRAM → async PRAM
Next: distributed computing with message passing and asynchrony:
- message delays
- arbitrary local clocks with sequence of execution \(s_0, a_1, s_1, a_2, s_2, a_3,\dots\)
- execution can be finite or infinite
- transition is a triplet: \(<s, a, s'>\) (\(s'\) is post-state)
- on each machine locally:
1 2 3 4 |
|
modellings a system this way is called a transition system. Changes to system occur locally, not globally. Actions of the system may be visible or not. For example sending a message is visible to an outside observer, but incrementing a local variable is not.
labelled transition system = actions are labelled and visible from outside
There are two classes of transitions: 1. external = visible from outside to the environment 2. internal = visible only locally, not to any other system component
Components are modeled as a set of transitions.
trace = a projection of execution on external actions
Example of trace
execution: \(S_0\), send, receive, \({i++}\), send...
- trace: send, receive, send
- what the world sees and observes about state, and excluding internal actions)
Input/Output Automata (IOA)
- Introduced in 1989 by Lynch & Tuttle.
- IOA is a labelled transition system that consists of a signature (actions with types), state, and transitions
- Initially a mathematical model for distributed systems → later followed by a programming language
- a transition is modeled in "precondition - effect" form
- if a state is such that message queue is not empty → take action → change state
Unordered lossless channel
A, B - clients and C - channel
- the channel provides a service to A, B
- the channel is unordered and lossless
- transitions are input/output actions
- a message interacts between component (A or B) and channel C
signature
- input: send (\(m\)), \(m \in T\)
- output: recv(\(m\), \(m \in T\)
state
- \(M \in\) messages (set), initially \(\varnothing\)
transitions (specified for channel)
- input \(send(m)\)
- precondition: true (channel cannot deny service)
- effect: \(M \leftarrow M \cup {m}\)
- output \(recv(m)\)
- precondition: \(m \in M\)
- effect: \(M \leftarrow M - {m}\)
Notes:
- if there are multiple components sending specify \(send(m)_{i,j}\)
- messages are specified in a set → channel is unordered
- message is removed from set only on delivery → channel is lossless
- sending increases the set of messages by 1
- there can be infinitely many transitions
Unordered lossy channel
We can make the previous specification lossy by adding a new transition:
transitions (for channel)
- input \(send(m) \dots\)
- output \(recv(m) \dots\)
- internal \(lose(m)\)
- precondition: \(m \in M\)
- effect: \(M \leftarrow M - {m}\)
Note that lose is not visible in the trace; it is internal only and invisible to observer of the system.
Ordered lossy channel
state
- \(M \in\) messages (queue), initially empty
transitions
- input \(send(m)\):
- precondition: true
- effect: \(M \leftarrow M \circ {m}\) (add to queue)
- output \(recv(m)\)
- precondition: \(m = M.first\)
- effect: \(M \leftarrow M.tail\)
- internal \(lose(m)\)
- precondition: \(m = M.first\)
- effect: \(M \leftarrow M.tail\)
For lossy channel it is sufficient to lose the message some time before delivery (cannot remove message from middle of queue) → lose first messages.
These examples are mathematical definitions of a channel.
Server example
on server - state: \(<lv, lts>\)
1 2 3 4 5 |
|
- state: \(<lv, lt, M>\) (where \(M\) is a set of messages)
- signature:
- input: \(recv(m)\)
- output: \(send(m)\)
- transitions:
- input \(recv(W, ts, v)\)
- precondition: true
- effect: (effect is the code block)
*) need to do
1 2 3
if ts > lts then: LV <- v, LT <- TS (* M <- M u {send_ack} (**
send_ack
but cannot nest actions → add queue to state
**) then can dosend_ack
- out \(send(m)\)
- precondition: \(m \in M\)
- effect: \(M \leftarrow M - {m}\)
To add ability to respond to read messages, specify input \(recv(R,...)\), with precondition and effect. Pattern matching allows distinguishing between reads and writes.
How to model failures on server? Introduce a fail action and send send and receive to check for crash.
- signature: input fail
- state: \(<lv, lt, M, crash>\)
- transitions:
- input \(fail\):
- precondition: true
- effect: crash = true
- input \(recv(m)\):
effect: (add)
if !crash && ts > lts then...
- output \(send(m)\): precondition: (add) \(m \in M\) and not crash
- input \(fail\):
Now we have a server that can fail. Recall safety means nothing bad happens, but since the specification is modeling failures, failures are allowed to happen. IOA models safety not liveness. There is nothing that forces progress. Prove safety, obtain a set of traces then can reason about liveness.