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
//! The [`Object`] trait every synchronization primitive implements, and the
//! [`Transition`] a strategy picks. This is the crate's extension point: implement
//! [`Object`] for your own primitive and register it with
//! [`World::register`](crate::World::register).
use ;
/// Index of an object in the [`World`](crate::World)'s object table, assigned in
/// registration order; doubles as the object's identity. A custom [`Object`]
/// receives its `ObjectID` from [`World::register`](crate::World::register) and stamps
/// it into every [`Transition`] it builds.
pub type ObjectID = usize;
/// One schedulable step: a process performing one observable operation on one
/// synchronization object — the unit the search picks at each scheduling point.
///
/// It carries the operating process, the target object, and a per-object `seq` that
/// tells the object's several concurrent operations apart. A custom [`Object`] builds
/// one with [`Transition::new`] when an awaited operation registers itself, stores it,
/// and hands the same value back from [`Object::enabled`]; the model later returns it
/// to [`Object::apply`]. Identity is by value, so an object matches a transition
/// simply with `==`.
/// A synchronization primitive as the model sees it: a small state machine whose
/// every observable operation is a schedulable [`Transition`].
///
/// Implement this to add your own primitive — a lock, a channel, a barrier — next
/// to the built-in [`Atomic`](crate::Atomic), then register it with
/// [`World::register`](crate::World::register). These four methods are the *entire*
/// contract between a primitive and the checker: everything the checker knows about
/// your primitive it learns through them.
///
/// # The operation lifecycle
///
/// The model never drives your futures directly. Instead, an awaited operation
/// should *register* itself — record its intent together with the process's
/// [`Waker`](std::task::Waker), build a [`Transition`] with [`Transition::new`],
/// and return [`Poll::Pending`](std::task::Poll::Pending) — and then report that
/// pending transition from [`enabled`](Object::enabled). The strategy drives
/// execution by choosing one enabled transition and calling
/// [`apply`](Object::apply), at which point the operation *commits*: it mutates the
/// object's state, records what happened so [`label`](Object::label) can describe
/// it, and wakes the process so its `.await` resolves. Splitting registration from
/// commit is what lets the strategy decide *when* each operation takes effect
/// relative to the others.
///
/// # Shared state
///
/// [`World::register`](crate::World::register) keeps one clone of your handle to drive
/// and hands the others to the processes, so a handle's clones **must** share one
/// underlying state — wrap it in an `Rc<RefCell<…>>`. A commit applied through the
/// model's clone has to be visible to the process holding another clone.
///
/// # Determinism
///
/// The checker rebuilds states by re-running the program and re-applying a trace, so
/// every method must be a deterministic function of the operations applied so far:
/// [`enabled`](Object::enabled) must list transitions in a fixed order, and `seq`
/// identities must be assigned the same way on every run.