8
8
(* *)
9
9
(* The model also verifies that no data races occur between the producers *)
10
10
(* and consumers and that all consumers eventually read all published *)
11
- (* values. *)
11
+ (* values (in a Multicast fashion - i.e. all consumers read all events). *)
12
12
(************************************************************************** *)
13
13
14
14
EXTENDS Integers , FiniteSets , Sequences
@@ -22,16 +22,19 @@ CONSTANTS
22
22
23
23
ASSUME Writers /= { }
24
24
ASSUME Readers /= { }
25
- ASSUME Size \in Nat \ { 0 }
25
+ ASSUME Size \in Nat \ { 0 }
26
+ ASSUME MaxPublished \in Nat \ { 0 }
26
27
27
28
VARIABLES
28
29
ringbuffer ,
29
30
next_sequence , (* Shared counter for claiming a sequence for a Writer. *)
30
31
claimed_sequence , (* Claimed sequence by each Writer. *)
31
32
published , (* Encodes whether each slot is published. *)
32
33
read , (* Read Cursors. One per Reader. *)
33
- consumed , (* Sequence of all read events by the Readers. *)
34
- pc (* Program Counter for each Writer/Reader. *)
34
+ pc , (* Program Counter for each Writer/Reader. *)
35
+ consumed (* Sequence of all read events by the Readers. *)
36
+ (* This is a history variable used for liveliness *)
37
+ (* checking. *)
35
38
36
39
vars == <<
37
40
ringbuffer ,
@@ -63,7 +66,12 @@ Range(f) ==
63
66
{ f [ x ] : x \in DOMAIN ( f ) }
64
67
65
68
MinReadSequence ==
66
- CHOOSE min \in Range ( read ) : \A r \in Readers : min <= read [ r ]
69
+ CHOOSE min \in Range ( read ) :
70
+ \A r \in Readers : min <= read [ r ]
71
+
72
+ MinClaimedSequence ==
73
+ CHOOSE min \in Range ( claimed_sequence ) :
74
+ \A w \in Writers : min <= claimed_sequence [ w ]
67
75
68
76
(* **************************************************************************)
69
77
(* Encode whether an index is published by tracking if the slot was *)
@@ -86,6 +94,20 @@ Publish(sequence) ==
86
94
\* Flip whether we're at an even or odd round.
87
95
IN published ' = [ published EXCEPT ! [ index ] = Xor ( TRUE , @ ) ]
88
96
97
+ (* **************************************************************************)
98
+ (* Computes the highest published sequence number that can be read. *)
99
+ (* This might seem strange but e.g. a producer P1 can be about to publish *)
100
+ (* sequence 5 while producer P2 has published sequence 6 and thus *)
101
+ (* consumers can neither read sequence 5 nor 6 (yet). *)
102
+ (************************************************************************** *)
103
+ AvailablePublishedSequence ==
104
+ LET guaranteed_published == MinClaimedSequence - 1
105
+ candidate_sequences == { guaranteed_published } \union Range ( claimed_sequence )
106
+ IN CHOOSE max \in candidate_sequences :
107
+ IsPublished ( max ) => ~ \E w \in Writers :
108
+ /\ claimed_sequence [ w ] = max + 1
109
+ /\ IsPublished ( claimed_sequence [ w ] )
110
+
89
111
(* **************************************************************************)
90
112
(* Producer Actions: *)
91
113
(************************************************************************** *)
@@ -98,10 +120,9 @@ BeginWrite(writer) ==
98
120
IN
99
121
\* Are we clear of all consumers? (Potentially a full cycle behind).
100
122
/\ min_read >= seq - Size
101
- /\ seq < MaxPublished
102
123
/\ claimed_sequence ' = [ claimed_sequence EXCEPT ! [ writer ] = seq ]
103
124
/\ next_sequence ' = seq + 1
104
- /\ Transition ( writer , Access , Advance )
125
+ /\ Transition ( writer , Advance , Access )
105
126
/\ Buffer ! Write ( index , writer , seq )
106
127
/\ UNCHANGED << consumed , published , read >>
107
128
@@ -110,7 +131,7 @@ EndWrite(writer) ==
110
131
seq == claimed_sequence [ writer ]
111
132
index == Buffer ! IndexOf ( seq )
112
133
IN
113
- /\ Transition ( writer , Advance , Access )
134
+ /\ Transition ( writer , Access , Advance )
114
135
/\ Buffer ! EndWrite ( index , writer )
115
136
/\ Publish ( seq )
116
137
/\ UNCHANGED << claimed_sequence , next_sequence , consumed , read >>
@@ -125,7 +146,7 @@ BeginRead(reader) ==
125
146
index == Buffer ! IndexOf ( next )
126
147
IN
127
148
/\ IsPublished ( next )
128
- /\ Transition ( reader , Access , Advance )
149
+ /\ Transition ( reader , Advance , Access )
129
150
/\ Buffer ! BeginRead ( index , reader )
130
151
\* Track what we read from the ringbuffer.
131
152
/\ consumed ' = [ consumed EXCEPT ! [ reader ] = Append ( @ , Buffer ! Read ( index ) ) ]
@@ -136,7 +157,7 @@ EndRead(reader) ==
136
157
next == read [ reader ] + 1
137
158
index == Buffer ! IndexOf ( next )
138
159
IN
139
- /\ Transition ( reader , Advance , Access )
160
+ /\ Transition ( reader , Access , Advance )
140
161
/\ Buffer ! EndRead ( index , reader )
141
162
/\ read ' = [ read EXCEPT ! [ reader ] = next ]
142
163
/\ UNCHANGED << claimed_sequence , next_sequence , consumed , published >>
@@ -148,11 +169,11 @@ EndRead(reader) ==
148
169
Init ==
149
170
/\ Buffer ! Init
150
171
/\ next_sequence = 0
151
- /\ claimed_sequence = [ w \in Writers |-> - 1 ]
152
- /\ published = [ i \in 0 .. Size |-> FALSE ]
153
- /\ read = [ r \in Readers |-> - 1 ]
154
- /\ consumed = [ r \in Readers |-> << >> ]
155
- /\ pc = [ a \in Writers \union Readers |-> Access ]
172
+ /\ claimed_sequence = [ w \in Writers |-> - 1 ]
173
+ /\ published = [ i \in 0 .. Buffer ! LastIndex |-> FALSE ]
174
+ /\ read = [ r \in Readers |-> - 1 ]
175
+ /\ consumed = [ r \in Readers |-> << >> ]
176
+ /\ pc = [ a \in Writers \union Readers |-> Advance ]
156
177
157
178
Next ==
158
179
\/ \E w \in Writers : BeginWrite ( w )
@@ -161,14 +182,18 @@ Next ==
161
182
\/ \E r \in Readers : EndRead ( r )
162
183
163
184
Fairness ==
164
- /\ \A w \in Writers : WF_ vars ( BeginWrite ( w ) )
165
- /\ \A w \in Writers : WF_ vars ( EndWrite ( w ) )
166
185
/\ \A r \in Readers : WF_ vars ( BeginRead ( r ) )
167
186
/\ \A r \in Readers : WF_ vars ( EndRead ( r ) )
168
187
169
188
Spec ==
170
189
Init /\ [] [ Next ]_ vars /\ Fairness
171
190
191
+ (* **************************************************************************)
192
+ (* State constraint - bounds model: *)
193
+ (************************************************************************** *)
194
+
195
+ StateConstraint == next_sequence <= MaxPublished
196
+
172
197
(* **************************************************************************)
173
198
(* Invariants: *)
174
199
(************************************************************************** *)
@@ -179,7 +204,7 @@ TypeOk ==
179
204
/\ Buffer ! TypeOk
180
205
/\ next_sequence \in Nat
181
206
/\ claimed_sequence \in [ Writers -> Int ]
182
- /\ published \in [ 0 .. Size -> { TRUE , FALSE } ]
207
+ /\ published \in [ 0 .. Buffer ! LastIndex -> { TRUE , FALSE } ]
183
208
/\ read \in [ Readers -> Int ]
184
209
/\ consumed \in [ Readers -> Seq ( Nat ) ]
185
210
/\ pc \in [ Writers \union Readers -> { Access , Advance } ]
@@ -188,8 +213,9 @@ TypeOk ==
188
213
(* Properties: *)
189
214
(************************************************************************** *)
190
215
191
- (* Eventually always, consumers must have read all published values. *)
216
+ (* Eventually always, consumers must have read all published values. *)
192
217
Liveliness ==
193
- <> [] ( \A r \in Readers : consumed [ r ] = [ i \in 1 .. MaxPublished |-> i - 1 ] )
218
+ \A r \in Readers : \A i \in 0 .. ( MaxPublished - 1 ) :
219
+ <> [] ( i \in 0 .. AvailablePublishedSequence => Len ( consumed [ r ] ) >= i + 1 /\ consumed [ r ] [ i + 1 ] = i )
194
220
195
221
=============================================================================
0 commit comments