@@ -6,56 +6,53 @@ import (
6
6
"time"
7
7
)
8
8
9
+ // Define the type for specifying the kind of entry in history.
9
10
type entryKind bool
10
11
12
+ // Constants for differentiating call and return entries.
11
13
const (
12
14
callEntry entryKind = false
13
15
returnEntry = true
14
16
)
15
17
18
+ // entry represents a single operation (call or return) in the history.
16
19
type entry struct {
17
20
kind entryKind
18
- value interface {}
19
- id uint
20
- time int64
21
+ value interface {} // Value associated with the operation.
22
+ id uint // Unique identifier for the operation.
23
+ time int64 // Timestamp of the operation.
21
24
}
22
25
26
+ // byTime implements sort.Interface for sorting entries by their timestamp.
23
27
type byTime []entry
24
28
25
- func (a byTime ) Len () int {
26
- return len (a )
27
- }
28
-
29
- func (a byTime ) Swap (i , j int ) {
30
- a [i ], a [j ] = a [j ], a [i ]
31
- }
32
-
33
- func (a byTime ) Less (i , j int ) bool {
34
- return a [i ].time < a [j ].time
35
- }
29
+ func (a byTime ) Len () int { return len (a ) }
30
+ func (a byTime ) Swap (i , j int ) { a [i ], a [j ] = a [j ], a [i ] }
31
+ func (a byTime ) Less (i , j int ) bool { return a [i ].time < a [j ].time }
36
32
33
+ // makeEntries converts a slice of Operations to a slice of entries, sorted by time.
37
34
func makeEntries (history []Operation ) []entry {
38
- var entries []entry = nil
35
+ var entries []entry
39
36
id := uint (0 )
40
37
for _ , elem := range history {
41
- entries = append (entries , entry {
42
- callEntry , elem .Input , id , elem .Call })
43
- entries = append (entries , entry {
44
- returnEntry , elem .Output , id , elem .Return })
38
+ entries = append (entries , entry {callEntry , elem .Input , id , elem .Call })
39
+ entries = append (entries , entry {returnEntry , elem .Output , id , elem .Return })
45
40
id ++
46
41
}
47
42
sort .Sort (byTime (entries ))
48
43
return entries
49
44
}
50
45
46
+ // node represents a node in a doubly linked list of entries.
51
47
type node struct {
52
- value interface {}
53
- match * node // call if match is nil, otherwise return
54
- id uint
55
- next * node
56
- prev * node
48
+ value interface {} // Value associated with the node.
49
+ match * node // Corresponding call/return node. For call entries, match is nil.
50
+ id uint // Unique identifier for the operation.
51
+ next * node // Next node in the list.
52
+ prev * node // Previous node in the list.
57
53
}
58
54
55
+ // insertBefore inserts a new node before the 'mark' node in the list.
59
56
func insertBefore (n * node , mark * node ) * node {
60
57
if mark != nil {
61
58
beforeMark := mark .prev
@@ -69,6 +66,7 @@ func insertBefore(n *node, mark *node) *node {
69
66
return n
70
67
}
71
68
69
+ // length calculates the length of the linked list starting from 'n'.
72
70
func length (n * node ) uint {
73
71
l := uint (0 )
74
72
for n != nil {
@@ -78,9 +76,10 @@ func length(n *node) uint {
78
76
return l
79
77
}
80
78
79
+ // renumber reassigns unique identifiers to events in the history.
81
80
func renumber (events []Event ) []Event {
82
81
var e []Event
83
- m := make (map [uint ]uint ) // renumbering
82
+ m := make (map [uint ]uint ) // Map for renumbering.
84
83
id := uint (0 )
85
84
for _ , v := range events {
86
85
if r , ok := m [v .Id ]; ok {
@@ -94,6 +93,7 @@ func renumber(events []Event) []Event {
94
93
return e
95
94
}
96
95
96
+ // convertEntries converts a slice of Events to a slice of entries.
97
97
func convertEntries (events []Event ) []entry {
98
98
var entries []entry
99
99
for _ , elem := range events {
@@ -106,9 +106,10 @@ func convertEntries(events []Event) []entry {
106
106
return entries
107
107
}
108
108
109
+ // makeLinkedEntries creates a doubly linked list of entries from a slice of entries.
109
110
func makeLinkedEntries (entries []entry ) * node {
110
111
var root * node = nil
111
- match := make (map [uint ]* node )
112
+ match := make (map [uint ]* node ) // Map to track corresponding call/return nodes.
112
113
for i := len (entries ) - 1 ; i >= 0 ; i -- {
113
114
elem := entries [i ]
114
115
if elem .kind {
@@ -125,11 +126,13 @@ func makeLinkedEntries(entries []entry) *node {
125
126
return root
126
127
}
127
128
129
+ // cacheEntry is used for caching the states encountered during the linearization check.
128
130
type cacheEntry struct {
129
- linearized bitset
130
- state interface {}
131
+ linearized bitset // Bitset representing the linearized operations.
132
+ state interface {} // State of the model after these operations.
131
133
}
132
134
135
+ // cacheContains checks if a given cache entry is already in the cache.
133
136
func cacheContains (model Model , cache map [uint64 ][]cacheEntry , entry cacheEntry ) bool {
134
137
for _ , elem := range cache [entry .linearized .hash ()] {
135
138
if entry .linearized .equals (elem .linearized ) && model .Equal (entry .state , elem .state ) {
@@ -139,11 +142,13 @@ func cacheContains(model Model, cache map[uint64][]cacheEntry, entry cacheEntry)
139
142
return false
140
143
}
141
144
145
+ // callsEntry represents an entry in the list of ongoing calls.
142
146
type callsEntry struct {
143
147
entry * node
144
- state interface {}
148
+ state interface {} // Model state after the call.
145
149
}
146
150
151
+ // lift removes an entry and its match from the linked list.
147
152
func lift (entry * node ) {
148
153
entry .prev .next = entry .next
149
154
entry .next .prev = entry .prev
@@ -154,6 +159,7 @@ func lift(entry *node) {
154
159
}
155
160
}
156
161
162
+ // unlift re-inserts an entry and its match back into the linked list.
157
163
func unlift (entry * node ) {
158
164
match := entry .match
159
165
match .prev .next = match
@@ -164,6 +170,7 @@ func unlift(entry *node) {
164
170
entry .next .prev = entry
165
171
}
166
172
173
+ // checkSingle checks if a single partition of the history is linearizable.
167
174
func checkSingle (model Model , subhistory * node , kill * int32 ) bool {
168
175
n := length (subhistory ) / 2
169
176
linearized := newBitset (n )
@@ -213,6 +220,7 @@ func checkSingle(model Model, subhistory *node, kill *int32) bool {
213
220
return true
214
221
}
215
222
223
+ // fillDefault fills in default implementations for missing methods in the model.
216
224
func fillDefault (model Model ) Model {
217
225
if model .Partition == nil {
218
226
model .Partition = NoPartition
@@ -226,14 +234,12 @@ func fillDefault(model Model) Model {
226
234
return model
227
235
}
228
236
237
+ // CheckOperations checks if the operations in the history are linearizable.
229
238
func CheckOperations (model Model , history []Operation ) bool {
230
239
return CheckOperationsTimeout (model , history , 0 )
231
240
}
232
241
233
- /*
234
- * Timeout = 0 means no timeout if this operation times out, then a false positive is possible
235
- */
236
-
242
+ // CheckOperationsTimeout checks if the operations in the history are linearizable with a timeout.
237
243
func CheckOperationsTimeout (model Model , history []Operation , timeout time.Duration ) bool {
238
244
model = fillDefault (model )
239
245
partitions := model .Partition (history )
@@ -271,14 +277,12 @@ loop:
271
277
return ok
272
278
}
273
279
280
+ // CheckEvents checks if the events in the history are linearizable.
274
281
func CheckEvents (model Model , history []Event ) bool {
275
282
return CheckEventsTimeout (model , history , 0 )
276
283
}
277
284
278
- /*
279
- * timeout = 0 means no timeout if this operation times out, then a false positive is possible
280
- */
281
-
285
+ // CheckEventsTimeout checks if the events in the history are linearizable with a timeout.
282
286
func CheckEventsTimeout (model Model , history []Event , timeout time.Duration ) bool {
283
287
model = fillDefault (model )
284
288
partitions := model .PartitionEvent (history )
0 commit comments