-
Notifications
You must be signed in to change notification settings - Fork 5
/
ClockSync6.tla
169 lines (150 loc) · 5.04 KB
/
ClockSync6.tla
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
------------------------------ MODULE ClockSync6 ------------------------------
(*
* Incremental TLA+ specification of the clock synchronization algorithm from:
*
* Hagit Attiya, Jennifer Welch. Distributed Computing. Wiley Interscience, 2004,
* p. 147, Algorithm 20.
*
* Assumptions: timestamps are natural numbers, not reals.
*
* Version 6: Make Proc a parameter and use ApaFoldSet
* Version 5: Ignoring the message by the process itself
* Version 4: Adjusting clock values (test 4 + invariant violation)
* Version 3: Receiving messages (test 3)
* Version 2: Sending messages (test 2)
* Version 1: Setting up the clocks (test 1)
*)
EXTENDS Integers, FiniteSets, Apalache
CONSTANTS
\* the set of processes
\* @type: Set(Str);
Proc,
\* minimum message delay
\* @type: Int;
t_min,
\* maximum message delay
\* @type: Int;
t_max
ASSUME(t_min >= 0 /\ t_max > t_min)
VARIABLES
\* the reference clock, inaccessible to the processes
\* @type: Int;
time,
\* hardware clock of a process
\* @type: Str -> Int;
hc,
\* clock adjustment of a process
\* @type: Str -> Int;
adj,
\* clock diff for process j, as seen by a process j
\* @type: <<Str, Str>> -> Int;
diff,
\* messages sent by the processes
\* @type: Set([src: Str, ts: Int]);
msgs,
\* messages received by the processes
\* @type: Str -> Set([src: Str, ts: Int]);
rcvd,
\* the control state of a process
\* @type: Str -> Str;
state
(***************************** DEFINITIONS *********************************)
\* the number of processes
NProc == Cardinality(Proc)
\* control states
State == { "init", "sent", "sync" }
\* the adjusted clock of process i
AC(i) == hc[i] + adj[i]
\* sum up the clock differences as observed by a process p
\* @type: (<<Str, Str>> -> Int, Str) => Int;
DiffSum(df, p) ==
LET Add(total, q) ==
total + df[p, q]
IN
ApaFoldSet(Add, 0, Proc)
(*************************** INITIALIZATION ********************************)
\* Initialization
Init ==
/\ time \in Nat
/\ hc \in [ Proc -> Nat ]
/\ adj = [ p \in Proc |-> 0 ]
/\ diff = [ <<p, q>> \in Proc \X Proc |-> 0 ]
/\ state = [ p \in Proc |-> "init" ]
/\ msgs = {}
/\ rcvd = [ p \in Proc |-> {} ]
(******************************* ACTIONS ***********************************)
\* send the value of the hardware clock
SendMsg(p) ==
/\ state[p] = "init"
/\ msgs' = msgs \union { [ src |-> p, ts |-> hc[p] ] }
/\ state' = [ state EXCEPT ![p] = "sent" ]
/\ UNCHANGED <<time, hc, adj, diff, rcvd>>
\* If the process has received a message from all processes (but p),
\* then adjust the clock. Otherwise, accumulate the difference.
\* @type: (Str, <<Str, Str>> -> Int,
\* Set([src: Str, ts: Int])) => Bool;
AdjustClock(p, newDiff, newRcvd) ==
LET fromAll == { m.src: m \in newRcvd } = Proc \ { p } IN
IF fromAll
THEN
\* Assuming that Proc = { "p1", "p2" }.
\* See ClockSync5 for the general case.
/\ adj' = [ adj EXCEPT ![p] = DiffSum(newDiff, p) \div NProc ]
/\ state' = [ state EXCEPT ![p] = "sync" ]
ELSE
UNCHANGED <<adj, state>>
\* Receive a message sent by another process.
\* Adjust the clock if the message has been received from all processes.
ReceiveMsg(p) ==
/\ state[p] = "sent"
/\ \E m \in msgs:
/\ m \notin rcvd[p]
/\ m.src /= p \* ignore the message by p itself
\* the message cannot be received earlier than after t_min
/\ hc[m.src] >= m.ts + t_min
\* accumulate the difference and adjust the clock if possible
/\ LET delta == m.ts - hc[p] + (t_min + t_max) \div 2 IN
LET newDiff == [ diff EXCEPT ![p, m.src] = delta ] IN
LET newRcvd == rcvd[p] \union { m } IN
/\ AdjustClock(p, newDiff, newRcvd)
/\ rcvd' = [ rcvd EXCEPT ![p] = newRcvd ]
/\ diff' = newDiff
/\ UNCHANGED <<time, hc, msgs>>
\* let the time flow
AdvanceClocks(delta) ==
/\ delta > 0
\* clocks can be advanced only if there is no pending message
/\ \A m \in msgs:
hc[m.src] + delta > t_max =>
\A p \in Proc:
m \in rcvd[m.src]
\* clocks are advanced uniformly
/\ time' = time + delta
/\ hc' = [ p \in Proc |-> hc[p] + delta ]
/\ UNCHANGED <<adj, diff, msgs, state, rcvd>>
\* all actions together
Next ==
\/ \E delta \in Int:
AdvanceClocks(delta)
\/ \E p \in Proc:
\/ SendMsg(p)
\/ ReceiveMsg(p)
(******************************* PROPERTIES **********************************)
\* Theorem 6.15 from AW04:
\* Algorithm achieves u * (1 - 1/n)-synchronization for n processors.
SkewInv ==
LET allSync ==
\A p \in Proc: state[p] = "sync"
IN
LET boundedSkew ==
LET bound ==
\* extend the bound by NProc to account for rounding errors
(t_max - t_min) * (NProc - 1) + NProc * NProc
IN
\A p, q \in Proc:
LET df == AC(p) - AC(q)
IN
-bound <= df * NProc /\ df * NProc <= bound
IN
allSync => boundedSkew
===============================================================================