A Sliding Window Protocol: Technische Universiteit Eindhoven Department of Mathematics and Computer Science
A Sliding Window Protocol: Technische Universiteit Eindhoven Department of Mathematics and Computer Science
MASTER’S THESIS
iii
Contents
1 Introduction 1
1.1 Problem description . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.2 Methods used . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.3 Notation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
1.4 Terminology . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
iv
3.4.2 Implementation . . . . . . . . . . . . . . . . . . . . . . . . . 18
3.4.3 Progress . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
3.5 Recycling indices . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
3.5.1 Reducing indices on channel F . . . . . . . . . . . . . . . . . 21
3.5.2 Reducing indices on channel G . . . . . . . . . . . . . . . . . 24
3.5.3 Implementing cyclic indices . . . . . . . . . . . . . . . . . . 26
3.6 Limiting the size of sets . . . . . . . . . . . . . . . . . . . . . . . . . 27
v
Chapter 1
Introduction
This problem is usually solved by a so-called “sliding window protocol” [Ste76, Sne95].
We will derive such a sliding window protocol and prove that it complies to all the given
demands. We also show that our protocol is more general than [Sne95], and that the
proof is more complete than [Ste76].
1
To be able to completely prove the correctness of the protocol using Owicki/Gries
theory, the faulty channels are first modelled as a set of program operations in Chapter
2. This model is then used to derive the protocol in Chapter 3.
1.3 Notation
The program notation used in this thesis is derived from the Guarded Command Lan-
guage (GCL), and is the same as the notation used in [FG99].
In addition, these notations are used:
• Intervals of integers: for integers a and b, [a..b) denotes the set {i | a ≤ i < b }.
• Array segments: for any array X and natural numbers a and b that are at most
the size of X, X[a..b) denotes the array segment that contains all array elements
of X with an index in interval [a..b).
• Assignments using a proposition: x : P is a statement that assigns a value to
variable x such that proposition P holds. It is only defined if (∃x :: P) holds.
• Channel names: because multiple channels are used, we distinguish the vari-
ables used by these channels by prefixing variables and procedures that operate
on these variables with a channel name, e.g., F.ct is a different variable from
G.ct, and F.Recei ve is equal to the procedure Recei ve with all variable names
prefixed by F.
1.4 Terminology
This terminology is used throughout the document:
2
Chapter 2
ii4 F UUUUUU
iiiiii UU*
89:;
?>=< 89:;
?>=<
A jUUUU ii B
UUUU iiii
G t
i i
Figure 2.1 shows an example setup, consisting of two processes, A and B, and two
channels, F and G. The direction of the communication over the channels is signified
by the arrows: A is the sending process of F and the receiving process of G, B is the
receiving process of F and the sending process of G.
F.Send(b)
3
This operation is guaranteed to terminate.
2.2.2 Receiving
Receiving a message is modelled by the Recei ve-operation. Receiving a message from
a channel F into variable y can be implemented by the receiving process as follows:
F.Recei ve(y)
This operation is not guaranteed to terminate: Recei ve will not end until a message is
received.
4
2.3.1 Formalization of the desired features
To represent sent messages, we introduce, for each channel, an (unbounded) array C
that contains all sent messages for that channel and a variable ct : N that represents the
number of messages already sent, where ct = 0 initially. The array segment C[0..ct)
represents all sent messages.
A channel has feature 1 if for a variable y, y ∈ C[0..ct) is a correct postassertion
of Recei ve(y).
2.4.1 Send
The Send-operation is easy to implement, given the array C and variable ct introduced
in 2.3.1. Sending a message b is implemented by:
Send(b) : C.ct := b
; ct := ct + 1
2.4.2 Receive
C[0..ct) contains all sent messages. We introduce a variable cb : N that represents
how many message have already been received. Initially, cb = 0. As messages will
be received in the same order as they are sent, cb also represents the index of the next
message to be received. Receiving a message in variable y can be implemented as
follows:
Note that the Recei ve-operation establishes the desired postassertion y ∈ C[0..ct),
and that none of the assertions in the operation can be disturbed by a Send-operation,
making them globally correct.
5
2.5 Implementation of a faulty channel
2.5.1 Loss
By increasing cb before the guarded skip in the Recei ve-operation, messages with an
index at least the previous value of cb and smaller than the new value of cb will be lost.
We introduce a variable th : N to signify that the next th messages to be delivered will
be lost. This can be established by choosing a value for th and increasing cb by th:
Recei ve(y) : th : 0 ≤ th
; cb := cb + th
; if cb < ct → ski p fi
; { cb < ct }
y := C.cb
; { y ∈ C[0..ct) }
cb := cb + 1
{ y ∈ C[0..ct) }
The channel will not lose infinitely many messages, because th is a natural number. In-
terval [0..cb) now represents the indices of messages that will not be received anymore
(messages that have been received or lost).
2.5.2 Duplication
Receiving a message always increases cb, making it impossible for message cb to be
received multiple times. By omitting the increase of cb at the end of the Recei ve-
operation, messages can be duplicated: if th = 0, cb is not increased, and C.cb can be
received again.
Recei ve(y) : th : 0 ≤ th
; cb := cb + th
; if cb < ct → ski p fi
; { cb < ct }
y := C.cb
{ y ∈ C[0..ct) }
However, this duplication is not finite. If the value 0 is consistently chosen for th,
message C.cb is duplicated infinitely many times.
To make duplication finite, we introduce an unbounded array D[0..∞) of natural
numbers, which is assumed to initially contain an arbitrary number larger than 0 for
every possible index. Array D represents the number of times a message can be re-
ceived: a message C.i will be received at most D.i times. Every number in array D is
initially larger than 0 in order to keep losing and duplicating messages separate.
6
Finite duplication can now be implemented in the Recei ve-operation by decreasing
D.cb after receiving a message, and increasing cb if D.cb = 0.
Recei ve(y) : th : 0 ≤ th
; cb := cb + th
; do D.cb = 0 → cb := cb + 1 od { (note 0) }
; { D.cb > 0 }
if cb < ct → ski p fi
; { cb < ct } { D.cb > 0 }
y := C.cb
; { cb < ct } { D.cb > 0 } { y ∈ C[0..ct) }
D.cb := D.cb − 1
{ y ∈ C[0..ct) }
The added assertions are globally correct, because Send does not modify D.
This implementation keeps
invariant. K 0 holds initially, because all elements of D are initially larger than 0.
Statement ct := ct + 1 in Send does not disturb K 0. Statement D.cb := D.cb − 1 in
Recei ve does not disturb K 0, because cb < ct is a valid preassertion of that statement.
(note 0): This loop terminates. To see this, consider this variant function:
(↓ i : cb ≤ i ∧ D.i > 0 : i ) − cb
The domain of the minimum in this variant function is not empty, because of K 0. The
value of this variant function is not changed by the Send-operation. Every iteration of
the loop decreases the variant function by 1, and if it is 0, this implies D.cb > 0.
2.5.3 No FIFO
Because the Recei ve-operation always receives the message with index cb and because
cb is only increasing, values are received in the same order as they are sent. It is of
course possible to receive a message with an arbitrary index from [0..ct), but that would
give too much freedom: in paragraph 2.5.1, we concluded that [0..cb) represented
the indices of messages that would not be received anymore. The method for losing
messages is based on this property. Messages with an index i for which D.i = 0 should
also not be received anymore.
To keep the previously introduced features of finite loss and finite duplication, but
lose the FIFO property, an arbitrary index h is chosen, such that cb ≤ h < ct and
D.h > 0. To accomplish this, we introduce local variable h : N.
Recei ve(y) : th : 0 ≤ th
7
; cb := cb + th
; do D.cb = 0 → cb := cb + 1 od
; { D.cb > 0 }
if cb < ct → ski p fi
; { cb < ct } { D.cb > 0 }
h : cb ≤ h < ct ∧ D.h > 0
; y := C.h
; D.h := D.h − 1
{ y ∈ C[0..ct) }
This change will allow messages to be received in a different order than the one in
which they were sent. The finite loss and finite duplication features that were added
earlier are still present: messages can be lost because cb is increased, and messages
can be duplicated because one index can be selected more than once. Because D is
an array of natural numbers, duplication is finite; because cb is increased by a natural
number, loss is finite.
8
The queried preassertion Q(y := b) indicates that the rule of import and export
applies to this implementation of faulty channels.
K2 : ct − cb ≤ M
K2 : ct ≤ M + cb
Send(b) : C.ct := b
; { ? ct < M + cb }
ct := ct + 1
Send(b) : C.ct := b
; if ct < M + cb → ct := ct + 1 fi
This means that Send is able to block the calling process, which is not desired. To
avoid this, the oldest message in the buffer is removed when the buffer is ’full’, by
increasing cb:
Send(b) : C.ct := b
; if ct < M + cb → ct := ct + 1
ct = M + cb → ct, cb := ct + 1, cb + 1
fi
This introduces a new ’loss’ of messages, but the loss is finite: ct = M + cb im-
plies cb < ct, causing a Recei ve-operation to either receive a message or increase cb
(establishing ct < M + cb).
The modified Send-operation can disturb assertion D.cb > 0 in Recei ve. Luckily,
this assertion can safely be replaced by (∃i : cb ≤ i < ct : D.i > 0) which is
locally correct in Recei ve. This assertion is not disturbed by assignment ct, cb :=
9
ct + 1, cb + 1 in Send, because that assignment establishes D.(ct −1) > 0 due to the
invariance of K 0.
Recei ve(y) : th : 0 ≤ th
; cb := cb + th
; do D.cb = 0 → cb := cb + 1 od
; { (∃i : cb ≤ i < ct : D.i > 0) }
if cb < ct → ski p fi
; { cb < ct } { (∃i : cb ≤ i < ct : D.i > 0) }
h : cb ≤ h < ct ∧ D.h > 0
; y := C.h
; D.h := D.h − 1
{ y ∈ C[0..ct) }
10
Chapter 3
3.1 Introduction
We derive a protocol that enables the reliable transfer of a data stream from one process
to another process, and that conforms to the requirements from 1.1. The processes are
only allowed to communicate via the faulty channels model described in Chapter 2.
We will first develop an algorithm to transfer a finite stream of data in a finite
amount of time from one process to another process. We will use this algorithm as a
foundation for an algorithm that transfers an inifinite stream of data.
3.2 Situation
Consider two processes, A and B. A is to transfer data to B. The only way of com-
munication that is allowed between A and B is the usage of faulty channels. Because
the channels are faulty, at least two channels are needed: one channel for A to transfer
data to B, and one channel that allows B to acknowledge reception of data. The types
of the messages that are transferred over the channels is not important at this moment.
The two channels are named F and G. Process A is the sending process of F and
the receiving process of G, process B is the receiving process of F and the sending
process of G. The situation is the same as the one depicted in figure 2.1.
R0 : X[0..N) = Y [0..N)
11
We consider R0 to be a postcondition of A; if the postcondition has been established,
A can stop sending messages.
3.3.2 Implementation
An obvious suggestion for an invariant for A is:
P0 : X[0..q) = Y [0..q)
P1 : 0≤q≤N
A: do q 6= N →
{ ? X.q = Y.q (note 0) }
q := q + 1
od
{q = N }
(note 0): Y is a local variable of B and can neither be inspected nor changed by A. The
assertion can therefore not be established by inspecting or changing Y . Assume that
the indices i for which X.i = Y.i holds are known. We introduce a set Ackd of natural
numbers that contains these indices. This can be captured in an invariant:
Using P2, the queried assertion X.q = Y.q can be established — assuming global
correctness will not be disturbed by other components — by using a guarded skip:
A: do q 6= N →
if q ∈ Ackd → ski p fi
; { ♥ X.q = Y.q }
q := q + 1
od
{q = N }
P3 : [0..q) ⊆ Ackd
To enable progress, elements will have to be added to Ackd. We will not introduce a
new component to do this; expanding Ackd can be done synchronously with q := q+1,
because test q ∈ Ackd is only useful after changing Ackd. The value of a new local
12
variable of A, r : N, is added to Ackd:
A: do q 6= N →
; { ? X.r = Y.r ∧ 0 ≤ r < N (note 0) }
Ackd := Ackd ∪ {r }
; if q ∈ Ackd → ski p fi
; { ♥ X.q = Y.q }
q := q + 1
od
{q = N }
(note 0): This assertion is necessary to keep P2 invariant. A can only communicate
with B via channels F and G. This means that this assertion can only be established
locally by applying the rule of import and export on channel G. Global correctness is
established by never assigning a value other than X.i to Y.i , for i : 0 ≤ i < N.
What is the type of the messages transferred via channel G? The simplest form of
an acknowledgement is the single index i , that represents the boolean value X.i = Y.i .
We decide to use single indices as acknowledgements. This allows messages received
via G to be added directly to Ackd:
A: do q 6= N →
G.Recei ve(r )
; { ♥ X.r = Y.r ∧ 0 ≤ r < N }
Ackd := Ackd ∪ {r }
; if q ∈ Ackd → ski p fi
; { X.q = Y.q }
q := q + 1
od
{q = N }
This construction allows for deadlock: the element added to Ackd does not nec-
essarily equal q. In that case, the process blocks. To avoid this, the guarded skip is
replaced by a loop:
A: do q 6= N →
G.Recei ve(r )
; { X.r = Y.r ∧ 0 ≤ r < N }
Ackd := Ackd ∪ {r }
; do q ∈ Ackd →
{ X.q = Y.q }
; q := q + 1
od
od
{q = N }
To allow for progress in A, B will have to send indices via G. Applying the rule of
13
import and export suggests this structure for B, where p is a local variable of B.
B: ∗[ { ? X. p = Y. p ∧ 0 ≤ p < N (note 0) }
G.Send( p)
]
(note 0): The first conjunct of this assertion can be established by assignment Y. p :=
X. p, but X is a local variable of A and can neither be inspected nor changed by B.
Therefore, a new local variable m of B is introduced. The value of m is assigned to
Y. p:
B: ∗[ { ? X. p = m ∧ 0 ≤ p < N (note 1) }
Y. p := m
; { ♥ X. p = Y. p ∧ 0 ≤ p < N (note 0) }
G.Send( p)
]
(note 0): This assertion is globally correct, because Y and p are local variables, and X
is a constant.
(note 1): This assertion can only be established by communication with A, because
X is a local variable of A. Locally, this assertion can be established by applying the
rule of import and export on channel F.
What is the type of the messages transferred via channel F? A single element of X
is insufficient; how can the value of p be extracted from this element? The value i for
which X.i = m holds has to be available for that. To accomplish this, we assume that
all messages received via channel F consist of a pair ha, i i, where a is an element of
X, and i is the index of that element.
(note 0): This assertion is globally correct, because p and m are local variables, and X
is a constant.
To allow for progress in B, A will have to send messages via F. This can be done
in parallel with receiving the acknowledgements in A. The structure of A is changed
to obtain a parallel composition with two components:
A: do q 6= N → k do q 6= N →
A1 k A2
od k od
where A1 is the program fragment that processes the acknowledgements (the code is
equal to the code previously assigned to A):
14
A1 : G.Recei ve(r )
; { X.r = Y.r ∧ 0 ≤ r < N }
Ackd := Ackd ∪ {r }
; do q ∈ Ackd →
{ X.q = Y.q }
; q := q + 1
od
A2 sends messages via F. The rule of import and export dictates the following structure
for A2:
A2 : { ? X.t = b ∧ 0 ≤ t < N (note 0) }
F.Send(hb, ti)
(note 0): The first conjunct of this assertion can of course be established by replacing
b with X.t.
A2 : { ? X.t = X.t ∧ 0 ≤ t < N (note 0) }
F.Send(hX.t, ti)
(note 0): The second conjunct of this assertion is established by assigning a suitable
value to t. A possible choice for t is q, but that would be unnecessarily hampering the
throughput: it would mean that every message is sent repeatedly until an acknowledge-
ment for that message is received. Only then another message will be sent.
It seems more efficient to allow other messages than X.q to be sent; it also allows
for more strategic freedom in choosing the value to be sent. It is useless to resend
messages for which an acknowledgement has already been received. Therefore, a value
for t is chosen from [0..N) \ Ackd.
A2 : { ? [0..N)\Ackd 6= ∅ (note 0) }
t : t ∈ [0..N) \ Ackd
; { ♥ X.t = X.t ∧ 0 ≤ t < N }
F.Send(hX.t, ti)
(note 0): This assertion can be established locally by using a guarded skip, but is not
globally correct, because elements are added to Ackd in A1. To avoid deadlock, we
introduce a new set AckdCopy, to which Ackd is copied by A2, causing invariance of
P4 : AckdCopy ⊆ Ackd
A2 : { ? [0..N)\AckdCopy 6= ∅ (note 0) }
t : t ∈ [0..N) \ AckdCopy
; { X.t = X.t ∧ 0 ≤ t < N }
F.Send(hX.t, ti)
; AckdCopy := Ackd
15
(note 0): This assertion is established by replacing the guard of the loop that surrounds
A2 by [0..N)\AckdCopy 6= ∅:
A: do q 6= N → k do [0..N)\AckdCopy 6= ∅ →
A1 k A2
od k od
[0..N)\AckdCopy = ∅
≡ { set theor y }
[0..N) ⊆ AckdCopy
⇒ { P4, tr ansi ti vi t y o f ⊆ }
[0..N) ⊆ Ackd
⇒ { P2 }
(∀i : 0 ≤ i < N : X.i = Y.i )
≡ { de f R0 }
R0
The postcondition is implied if [0..N)\AckdCopy = ∅, meaning that the loop can end
without affecting the progress. Moreover, the loop ends if q = N, because q = N ⇒
[0..N) ⊆ Ackd holds because of the invariance of P3; the loop will end after copying
Ackd to AckdCopy.
3.3.3 Progress
To prove progress in this implementation, we will first prove that there is no individual
deadlock in the components of the system as long as the postcondition R0 is not estab-
lished. This is not enough to prove progress: it doesn’t prove that the repetitions in A
will terminate.
From the structure of A1 it is clear that q = N, which will terminate both repeti-
tions (see the end of the previous section), will be established if Ackd = [0..N) holds.
This can only be established if the set Ackd is expanded, which cannot be concluded
from the assertions in A1.
This is why we present a variant function that increases in all situations and has
an upper bound that implies Ackd = [0..N). Because there is no individual deadlock,
the variant function will increase, assuring progress. It is sufficient to only consider
individual deadlock; starvation does not occur, because all guards become stably true.
16
• There is no danger of individual deadlock in B, as long as A repeatedly sends
messages over channel F, because of the ’finite loss’ feature of the channels.
The component in A that contains A2 is a loop that doesn’t terminate until
[0..N)\AckdCopy = ∅, which implies R0 (see 3.3.2). A2 sends one message
in every iteration of that loop. There is no deadlock in B if there is no deadlock
in A2.
• There is no danger of individual deadlock in A2, because there are no blocking
statements.
17
– If F.Recei ve(hm, pi) has established p ∈
/ Ackd, this is not a stable con-
dition, but it can only be changed by expanding Ackd, which means an
increase of v f .
Because the last two parts of v f have an upper bound of 0, and the variant function
increases with every execution of A1, # AckdCopy +# Ackd will increase in finite time.
# AckdCopy and # Ackd both have an upper bound of N (because of the invariance of
P2 and P4).
3.4.2 Implementation
The algorithm from 3.3 is changed to transfer elements with indices [0 .. q+N) instead
of [0..N). If q increases (that is to say there is progress), infinitely many messages will
be transferred.
The invariants from A as specified in 3.3 are updated to reflect this change (P1 and
P2 have been weakened):
P0 : X[0..q) = Y [0..q)
P1 : 0≤q
P2 : (∀i : i ∈ Ackd : X.i = Y.i ∧ 0 ≤ i < q + N)
P3 : [0..q) ⊆ Ackd
P4 : AckdCopy ⊆ Ackd
The structure of component A has to be changed, because A doesn’t have to end any-
more:
A : ∗[ k ∗[
A1 k A2
] k ]
Component A1 is updated as follows (only assertion (1) has been weakened, because
P2 has been weakened):
A1 : G.Recei ve(r )
; {X.r = Y.r ∧ 0 ≤ r < q + N (1)}
Ackd := Ackd ∪ {r }
; do q ∈ Ackd →
{X.q = Y.q (0)}
; q := q + 1
od
18
Weakening assertion (1) in A1 allows us to weaken assertions (2) and (3) in component
B (because of the rule of import and export):
B: ∗[ F.Recei ve(hm, pi)
; { X. p = m ∧ 0 ≤ p < q + N (3) }
Y. p := m
; { X. p = Y. p ∧ 0 ≤ p < q + N (2) }
G.Send( p)
]
In A2, it is now allowed (by the rule of import and export) to weaken assertion (4),
because assertion (3) in B has been weakened. This allows for an expansion of the
domain from which t is selected to [0 .. q + N).
A2 : { ? [0 .. q + N)\AckdCopy 6= ∅ (5) }
t : t ∈ [0 .. q + N) \ AckdCopy
; { X.t = X.t ∧ 0 ≤ t < q + N (4) }
F.Send(hX.t, ti)
; AckdCopy := Ackd
Assertion (5) is no longer established by the guard of the loop in A. It can be established
by using a guarded skip:
A2 : if [0 .. q + N)\AckdCopy 6= ∅ → ski p fi
; { ♥ [0 .. q + N)\AckdCopy 6= ∅ (5) }
t : t ∈ [0 .. q + N) \ AckdCopy
; { X.t = X.t ∧ 0 ≤ t < q + N (4) }
F.Send(hX.t, ti)
; AckdCopy := Ackd
Assertion (5) is globally correct: the only statement that can falsify it is q := q + 1 in
A1. A valid postassertion of q := q + 1 is q − 1 + N ∈ / AckdCopy:
(q − 1 + N ∈ / AckdCopy) (q := q + 1)
≡ {substi tuti on }
q+N ∈ / AckdCopy
⇐ { P4 }
q+N ∈ / Ackd
⇐ { pr edi cate logi c }
P2
The guarded skip does not cause deadlock, because
[0 .. q + N)\AckdCopy = ∅
≡ {set theor y }
[0 .. q + N) ⊆ AckdCopy
⇒ { P4 }
[0 .. q + N) ⊆ Ackd
⇒ { N ≥ 1, set theor y }
q ∈ Ackd
19
If q ∈ Ackd, A1 will increase q until q ∈ / Ackd, which implies q ∈ / AckdCopy and
will cause the guard to be stably true.
It’s now possible to speak of a sliding window protocol: A sends elements from
X that have indices in a certain integer interval, the “window”’, where q is the start of
the window and the window size is N. All elements that are before the window, with
indices less than q, have already been received (see P3). Whenever q is increased, the
window progresses.
3.4.3 Progress
According to 3.4.2, there is progress if q increases. Using the variant function defined
in 3.3.3, it can be proved that q increases.
The changes to the components do not introduce a possibility of individual dead-
lock, as described above. With the same arguments as in paragraph 3.3.3, it can now
be proved that v f increases. The only difference is that there’s no upper bound any-
more to the size of Ackd and AckdCopy and consequently to the variant function; this
corresponds with the changed specification of the problem, because there’s no upper
bound to the array that is to be transferred.
The invariance of P2 guarantees that expanding Ackd means that q is increasing.
If v f increases over time, this implies that # Ackd increases over time, because the last
two parts of v f have an upper bound of 0 and # AckdCopy can not increase without an
increase of # Ackd (because of P4). We conclude that the set Ackd grows because v f
increases, which means q increases and there is progress.
20
A1 : G.Recei ve(r )
; {0 ≤ r < q + N }
Ackd := Ackd ∪ {r }
; do q ∈ Ackd →
q := q + 1
od
A2 : if [0 .. q + N)\AckdCopy 6= ∅ → ski p fi
; { [0 .. q + N)\AckdCopy 6= ∅ }
t : t ∈ [0 .. q + N) \ AckdCopy
; {0 ≤ t < q + N }
F.Send(t)
; AckdCopy := Ackd
B: ∗[ F.Recei ve( p)
; {0 ≤ p < q + N }
G.Send( p)
]
The goal is to find expressions k and m such that k ≤ p < k + m is a valid
postassertion of F.Recei ve( p) in B and k ≤ r < k + m is a valid postassertion of
G.Recei ve(r ) in A1. This means that the indices transferred via the channels may be
reduced modulo m.
This is easily seen by taking into account that t < q + N is a preassertion of F.Send(t)
in A2 and the fact that q never decreases.
This is trivial to prove, because r ∈ G.C[0 .. G.ct) is a valid postassertion of G.Recei ve(r ),
and therefore a valid preassertion of Ackd := Ackd ∪ {r }.
To prove this invariant, we add a dummy variable qCopy : N to A2, which acts as a
local copy of q just like AckdCopy is a local copy of Ackd. Initially qCopy = q = 0,
21
and it is updated whenever AckdCopy is updated:
A2 : if [0 .. q + N)\AckdCopy 6= ∅ → ski p fi
; { [0 .. q + N)\AckdCopy 6= ∅ }
t : t ∈ [0 .. q + N) \ AckdCopy
; {0 ≤ t < q + N }
F.Send(t)
; AckdCopy, qCopy := Ackd, q
Note that the last assignment establishes (∀i : 0 ≤ i < ct : C.i < qCopy + N)
(because of P5), which also holds initially and is therefore a loop invariant of the loop
that contains A2, only disturbed by the increase of ct in F.Send(t).
Also note that
P8 : [0 .. qCopy) ⊆ AckdCopy
is invariant: it holds initially, and the assignment AckdCopy, qCopy := Ackd, q does
not disturb it because of P3.
Initially P7 holds, and it is only disturbed by the increase of ct in F.Send(t), which
has C.ct = t and t ∈ [0 .. q + N)\AckdCopy as valid preassertions. We derive:
P7(ct := ct + 1)
≡ {substi tuti on }
(∀i, j : 0 ≤ i ≤ j < ct + 1 : C.i − N < C. j )
≡ {spli t o f f j = ct, P7 }
(∀i : 0 ≤ i ≤ ct : C.i − N < C.ct)
≡ {1 ≤ N ⇒ C.ct − N < C.ct }
(∀i : 0 ≤ i < ct : C.i − N < C.ct)
⇐ {loop i nvari ant (∀i : 0 ≤ i < ct : C.i < qCopy + N) }
(∀i : 0 ≤ i < ct : qCopy − 1 < C.ct)
≡ {C.ct = t }
(∀i : 0 ≤ i < ct : qCopy − 1 < t)
≡ / AckdCopy, P8 }
{t ∈
tr ue
P9 : Rcvd ⊆ C[0..ct)
P10 : [0..s) ⊆ Rcvd
Initially, Rcvd = ∅ and s = 0 satisfy the invariants. The updating of Rcvd and s in B
22
can occur in a manner very similar to the updating of Ackd and q in A:
B: ∗[ F.Recei ve( p)
; {0 ≤ p < q + N }
Rcvd := Rcvd ∪ {p}
; do s ∈ Rcvd →
s := s + 1
od
G.Send( p)
]
It is trivial to see that P9 and P10 are not disturbed by these assignments.
With the help of the previously defined and proved invariants, it is now possible to
prove the main invariant of this section, that together with P5 will enable the use of
recycable indices on channel F:
P11(M := 1)
≡ {substi tuti on, ct > M }
(∀i : ct − 1 ≤ i < ct : s − N ≤ C.i )
≡ {one − poi nt domai n }
s − N ≤ C.(ct − 1)
≡ {algebr a }
s − 1 − N < C.(ct − 1)
≡ { P9 ∧ P10 ⇒ s − 1 ∈ C[0..ct), P7( j := ct −1) }
tr ue
P11(M := M + 1)
≡ {substi tuti on, ct ≥ M +1 }
(∀i : ct − M −1 ≤ i < ct : s − N − M ≤ C.i )
≡ {spli t o f f i = ct − M −1, P11 }
s − N − M ≤ C.(ct − M −1)
≡ {algebr a }
s − (C.(ct − M −1) + N) ≤ M
⇐ {set theor y }
[C.(ct − M −1)+ N .. s) ⊆ C[ct − M .. ct)
⇐ {note (0) }
[0..s) ⊆ C[0..ct)
≡ { P9, P10 }
tr ue
23
note (0):
[0..s) ⊆ C[0..ct)
≡ {set theor y }
[0..s) ⊆ C[0 .. ct − M) ∪ C[ct − M .. ct)
⇒ {set theor y }
[C.(ct − M −1)+ N .. s) ⊆ C[0 .. ct − M) ∪ C[ct − M .. ct)
≡ { P7( j := ct − M −1) ⇒ [C.(ct − M −1)+ N .. s) ∩ C[0 .. ct − M) = ∅ }
[C.(ct − M −1)+ N .. s) ⊆ C[ct − M .. ct)
This proves invariance of P11.
24
Using P12, some unnecessary content can be removed from channel G: since
s ≤ q + N and [0..q) ⊆ Ackd (see P3), it is not necessary to send acknowledgements
for indices smaller than s − N. Therefore B is modified as follows:
B: ∗[ F.Recei ve( p)
; {0 ≤ p < q + N }
Rcvd := Rcvd ∪ {p}
; do s ∈ Rcvd →
s := s + 1
od
if p ≥ s − N → G.Send( p)
p < s − N → ski p
fi
]
The following is now invariant:
P15 : (∀ j : 0 ≤ j < ct : (↑ i : [0..i ) ⊆ C[0 .. j +1) : i ) − N ≤ C. j )
Invariant P15 holds initially, and is not disturbed by the increase of ct in G.Send( p)
(valid preassertions of that increase are C.ct = p, p ≥ s−N, p ∈ Rcvd and s ∈ / Rcvd):
P15(ct := ct + 1)
≡ {substi tuti on }
(∀ j : 0 ≤ j < ct +1 : (↑ i : [0..i ) ⊆ C[0 .. j +1) : i ) − N ≤ C. j )
≡ {spli t o ff j = ct, P15 }
(↑ i : [0..i ) ⊆ C[0 .. ct +1) : i ) − N ≤ C.ct
⇐ { P12 ∧ C.ct = p ∧ p ∈ Rcvd }
(↑ i : [0..i ) ⊆ Rcvd : i ) − N ≤ p
≡ { P10 ∧ s ∈ / Rcvd }
s−N ≤ p
≡ { p ≥ s−N }
tr ue
P15 can be used to prove that this invariant holds:
P16 : (∀i : (ct − M) ↑ 0 ≤ i < ct : q − N − M ≤ C.i )
For ct ≤ M the invariance of P16 is easy to see, because P3 ∧ P6 ∧ ct ≤ M ⇒ q ≤
M ⇒ q − N − M < 0, and C contains only natural numbers.
For ct > M, The proof is by induction on M. For P16(M := 1):
P16(M := 1)
≡ {substi tuti on, ct > M }
(∀i : ct − 1 ≤ i < ct : q − N −1 ≤ C.i )
≡ {si ngle poi nt domai n }
q − N −1 ≤ C.(ct − 1)
⇐ { P3 ∧ P6 }
(↑ i : [0..i ) ⊆ C[0 .. ct) : i ) − N − 1 ≤ C.(ct − 1)
≡ { P15 }
tr ue
25
For P16(M := M +1), assuming P16 holds:
P16(M := M +1)
≡ {substi tuti on, ct ≥ M +1 }
(∀i : ct − M − 1 ≤ i < ct : q − N − M −1 ≤ C.i )
≡ {spli t o f f i = ct − M − 1, P16 }
q − N − M −1 ≤ C.(ct − M − 1)
⇐ { P15 }
q − N − M −1 ≤ (↑ i : [0..i ) ⊆ C[0 .. ct − M) : i ) − N
≡ {algebr a }
q − (↑ i : [0..i ) ⊆ C[0 .. ct − M) : i ) − 1 ≤ M
⇐ {set theor y }
[ (↑ i : [0..i ) ⊆ C[0 .. ct − M) : i )+1 .. q ) ⊆ C[ct − M .. ct)
≡ {de f. ↑, set theor y }
[ (↑ i : [0..i ) ⊆ C[0 .. ct − M) : i )+1 .. q ) ⊆ C[0..ct)
≡ ( P3 ∧ P6 }
tr ue
26
A1 : G.Recei ve(r )
; r := q + (r −q) mod (2N + M)
; {0 ≤ r < q + N }
Ackd := Ackd ∪ {r }
; do q ∈ Ackd →
q := q + 1
od
A2 : if [0 .. q + N)\AckdCopy 6= ∅ → ski p fi
; { [0 .. q + N)\AckdCopy 6= ∅ }
t : t ∈ [0 .. q + N) \ AckdCopy
; {0 ≤ t < q + N }
F.Send(t mod (2N + M) )
; AckdCopy, qCopy := Ackd, q
B: ∗[ F.Recei ve( p)
p := s + ( p−s) mod (2N + M)
; {0 ≤ p < q + N }
Rcvd := Rcvd ∪ {p}
; do s ∈ Rcvd →
s := s + 1
od
if p ≥ s − N → G.Send( p mod (2N + M) )
p < s − N → ski p
fi
]
27
A1 : G.Recei ve(r )
; r := q + (r −q) mod (2N + M)
; { X.r = Y.r ∧ 0 ≤ r < q + N }
if q ≤ r → Ackd N := Ackd N ∪ {r }
q > r → ski p
fi
; do q ∈ Ackd N →
{ X.q = Y.q }
; q, Ackd N := q + 1, Ackd N\{q}
od
The size of set AckdCopy can be limited in the same way: it is obvious that there
exists a set AckdCopy N = AckdCopy\[0..qCopy), where AckdCopy = AckdCopy N∪
[0..qCopy) (because of P8), and it suffices to maintain AckdCopy N.
The only statement in A that affects AckdCopy N is AckdCopy, qCopy := Ackd N∪
[0..q), q, for which this holds:
28
Rcvd N is maintained as follows:
B : ∗[ F.Recei ve( p)
p := s + ( p−s) mod (2N + M)
; {0 ≤ p < q + N }
if p ≥ s → Rcvd N := Rcvd N ∪ {p}
p < s → ski p
fi
; do s ∈ Rcvd N →
s, Rcvd N := s + 1, Rcvd N\s
od
if p ≥ s − N → G.Send( p mod (2N + M) )
p < s − N → ski p
fi
]
29
Chapter 4
4.1 Summary
In this thesis, we have shown how to model faulty channels that can reorder, lose
and duplicate messages as multiprograms. The modelled channels are ’finitely faulty’;
there is a limit to the number of messages that can be consecutively lost or duplicated.
Using this model, we have derived a provably correct protocol that enables the
reliable transfer of a data stream from one machine to another machine over a faulty
channel. We have proven that the protocol exhibits progress.
Furthermore, we have proven that it is possible to limit the memory requirements
of the protocol by recycling the indices contained in the messages that the protocol
transfers, and by limiting the size of the sets that the protocol maintains.
30
explicit by using natural numbers that represent the number of messages that can be
lost and duplicated consecutively (see 2.5.1 and 2.5.2).
The protocol developed in [Sne95] sends a (reduced) set of all received indices as
an acknowledgement, while our protocol sends only a single index. Sending a set could
be a more efficient solution, because if an acknowledgement is lost, the next received
acknowledgement will contain a superset of the lost acknowledgement (because the
channels are FIFO). Even if the channels are not FIFO, the acknowledgements still
carry more information and might increase efficiency.
Van de Snepscheut also proves that indices can be reduced modulo 2N; the result
can be compared to our own result of 2N + M if we use M = 1 (which is equal to a
FIFO-channel). The difference of 1 can be explained by taking into account that only
channel G requires the wider 2N + M reduction (channel F will do with 2N +(M −1),
as can be seen from invariants P11, P5 and P12). If the assumption is made that F
and G are both FIFO, G can also be reduced modulo 2N.
There is a proof of progress given for the sequential program that is first developed,
using a variant function; it is explained that the final, distributed program meets the
progress requirements of the original sequential program, because of the method used
to partition the sequential program into processes.
4.3 Conclusions
• The proof-by-design methods explored in [FG99] can be employed to develop a
sliding window protocol, and lead to a complete proof of correctness.
• By using a step-by-step approach to developing the protocol, working from the
31
desired result to the solution, an elegant implementation can be obtained where
the structure of the design can be explained in a clear manner.
• By modelling the faulty channels, their properties have been formalized, en-
abling a more formal proof of correctness and progress of the final protocol.
• The faulty channels model can be used to prove that the ’rule of import and ex-
port’ applies to faulty channels, which makes it easier to prove properties of the
messages transferred over the channels without introducing more system invari-
ants.
• Proving that indices can be recycled requires proving a substantial number of
invariants that are not necessary to prove the correctness of the protocol when
indices are not recycled.
32
Bibliography
[OG76] S. Owicki and D. Gries, An Axiomatic Proof Technique for Parallel Programs
I, Acta Informatica, Vol. 6, pp. 319-340, 1976.
[FG99] W.H.J. Feijen and A.J.M. van Gasteren, On a method of multiprogramming,
Springer Verlag, 1999.
[Sne95] J.L.A. van de Snepscheut, The Sliding-Window Protocol Revisited, Formal
Aspects of Computing, Vol. 7, pp. 3-17, 1995.
[Ste76] N.V. Stenning, A data transfer protocol, Computer Networks, Vol. 1, pp. 99-
110, 1976.
33