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
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
//! # Lock ordering enforcement at compile time
//!
//! This library contains types and traits to ensure that locks that are held at
//! the same time are acquired in the correct order. This lets code authors
//! verify, at compile time, that their code is free of deadlock opportunities.
//!
//! The way this works is by using traits in the [relation] module to define
//! orderings between marker types that represent different lock-levels. The core
//! logic lives in the [`LockedAt`] type; it uses trait bounds to ensure that
//! any acquisition of locks respects these orderings.
//!
//! # How it works
//!
//! This crate combines two ideas:
//! 1. acquiring and holding locks can be represented as mutable borrowing; and
//! 2. a correct lock ordering can be modeled as a directed graph of pairwise
//! before/after relationships.
//!
//! ## Representing locking using mutable state
//!
//! Within any call tree, a (non-reentrant) lock cannot be acquired more than
//! once at a time. Doing otherwise would lead to deadlock.
//!
//! To visualize this, look at the following code:
//! ```
//! let mutex = std::sync::Mutex::new(false);
//!
//! {
//! // in a function somewhere
//! let guard = mutex.lock().unwrap();
//! // do something with the value
//! drop(guard);
//! }
//! ```
//!
//! For every call tree we assign each lock some mutable state and pair every
//! acquisition of a lock with a mutable borrow of that state. What that looks
//! like here:
//! ```
//! # let mutex = std::sync::Mutex::new(false);
//! {
//! let mut state = ();
//!
//! let (guard, borrow) = (mutex.lock().unwrap(), &mut state);
//! // do something
//! drop((guard, borrow));
//! }
//! ```
//!
//! Now we can take advantage of Rust's enforcement of exclusivity for mutable
//! borrows. This lets us prevent at compile time what would otherwise be a run
//! time deadlock!
//! ```compile_fail
//! # let mutex = std::sync::Mutex::new(false);
//! {
//! let mut state = ();
//!
//! let (guard, borrow) = (mutex.lock().unwrap(), &mut state);
//! {
//! // This fails to compile because `state` is already mutably borrowed.
//! let (second_guard, second_borrow) = (mutex.lock().unwrap(), &mut state);
//! drop((second_guard, second_borrow));
//! }
//! drop((guard, borrow));
//! }
//! ```
//!
//! This only works so long as we make sure that the borrows of our mutable
//! marker state last as long as the actual borrows of the locked state. We can
//! enforce that in the type system by tying the two lifetimes together.
//! ```
//! fn lock_with_marker_state<'a, T>(
//! mutex: &'a std::sync::Mutex<T>,
//! borrow: &'a mut (),
//! ) -> (std::sync::MutexGuard<'a, T>, &'a mut ()) {
//! # unimplemented!()
//! }
//! ```
//!
//! ## Modeling lock ordering as a directed graph of orderings
//!
//! In order for a program to follow a consistent lock ordering, it must be
//! ensured that for any two locks held at the same time, they are acquired in
//! the same order in every possible tree of function calls. One way to achieve
//! this is to assign every lock an ordered "level". Then we ensure that at each
//! point in the code we only acquire locks with a higher level than the highest
//! currently held.
//!
//! Since we're using Rust, we can use marker types to represent levels and
//! traits to represent the ordering between them. That might look something like this:
//! ```
//! trait LockedBefore<L> {}
//!
//! struct Unlocked;
//! struct FirstLevel;
//! struct SecondLevel;
//!
//! impl LockedBefore<FirstLevel> for Unlocked {}
//! impl LockedBefore<SecondLevel> for Unlocked {}
//! impl LockedBefore<SecondLevel> for FirstLevel {}
//! ```
//!
//! That's not enough on its own, but we can define functions whose trait bounds
//! enforce our lock ordering:
//! ```
//! # trait LockedBefore<L> {}
//! struct HeldLockLevel<L>(std::marker::PhantomData<L>);
//!
//! fn acquire_lock<'a, CurrentLevel, L, T>(
//! lock: &'a (std::sync::Mutex<T>, std::marker::PhantomData<L>),
//! current_level: &'a mut HeldLockLevel<CurrentLevel>,
//! ) -> (std::sync::MutexGuard<'a, T>, &'a mut HeldLockLevel<L>)
//! where
//! CurrentLevel: LockedBefore<L>,
//! {
//! // ...
//! # unimplemented!()
//! }
//! ```
//!
//! These can prevent us from acquiring locks out of order. With the following
//! shared state:
//! ```
//! # struct FirstLevel;
//! # struct SecondLevel;
//! let first_mutex = (
//! std::sync::Mutex::new(true),
//! std::marker::PhantomData::<FirstLevel>,
//! );
//! let second_mutex = (
//! std::sync::Mutex::new('a'),
//! std::marker::PhantomData::<SecondLevel>,
//! );
//! ```
//! This works:
//! ```no_run
//! # trait LockedBefore<L> {}
//! # struct Unlocked;
//! # struct FirstLevel;
//! # struct SecondLevel;
//! # impl LockedBefore<FirstLevel> for Unlocked {}
//! # impl LockedBefore<SecondLevel> for Unlocked {}
//! # impl LockedBefore<SecondLevel> for FirstLevel {}
//! # struct HeldLockLevel<L>(std::marker::PhantomData<L>);
//! # fn acquire_lock<'a, CurrentLevel, L, T>(
//! # lock: &'a (std::sync::Mutex<T>, std::marker::PhantomData<L>),
//! # current_level: &'a mut HeldLockLevel<CurrentLevel>,
//! # ) -> (std::sync::MutexGuard<'a, T>, &'a mut HeldLockLevel<L>)
//! # where
//! # CurrentLevel: LockedBefore<L>,
//! # {
//! # unimplemented!()
//! # }
//! # let first_mutex = (
//! # std::sync::Mutex::new(true),
//! # std::marker::PhantomData::<FirstLevel>,
//! # );
//! # let second_mutex = (
//! # std::sync::Mutex::new('a'),
//! # std::marker::PhantomData::<SecondLevel>,
//! # );
//! let mut current_level = HeldLockLevel::<Unlocked>(std::marker::PhantomData);
//!
//! let (mut first_guard, mut current_level) = acquire_lock(&first_mutex, &mut current_level);
//! let (mut second_guard, mut current_level) = acquire_lock(&second_mutex, &mut current_level);
//! ```
//! Attempting to access locks out of order fails:
//! ```compile_fail
//! # trait LockedBefore<L> {}
//! # struct Unlocked;
//! # struct FirstLevel;
//! # struct SecondLevel;
//! # impl LockedBefore<FirstLevel> for Unlocked {}
//! # impl LockedBefore<SecondLevel> for Unlocked {}
//! # impl LockedBefore<SecondLevel> for FirstLevel {}
//! # struct HeldLockLevel<L>(std::marker::PhantomData<L>);
//! # fn acquire_lock<'a, CurrentLevel, L, T>(
//! # lock: &'a (std::sync::Mutex<T>, std::marker::PhantomData<L>),
//! # current_level: &'a mut HeldLockLevel<CurrentLevel>,
//! # ) -> (std::sync::MutexGuard<'a, T>, &'a mut HeldLockLevel<L>)
//! # where
//! # CurrentLevel: LockedBefore<L>,
//! # {
//! # unimplemented!()
//! # }
//! # let first_mutex = (
//! # std::sync::Mutex::new(true),
//! # std::marker::PhantomData::<FirstLevel>,
//! # );
//! # let second_mutex = (
//! # std::sync::Mutex::new('a'),
//! # std::marker::PhantomData::<SecondLevel>,
//! # );
//! let mut current_level = HeldLockLevel::<Unlocked>(std::marker::PhantomData);
//!
//! let (mut second_guard, mut current_level) = acquire_lock(&second_mutex, &mut current_level);
//! // This will fail to compile because SecondLevel does not implement LockedBefore<FirstLevel>
//! let (mut first_guard, mut current_level) = acquire_lock(&first_mutex, &mut current_level);
//! ```
//!
//! It's worth noting that this only works so long as we ensure our
//! `LockedBefore` trait implementations are consistent. There's no
//! language-level feature that prevents us from introducing a cycle in our
//! trait implementations and defining an invalid lock ordering!
//!
//! # How to use this crate
//!
//! You'll need to define a marker type for each "level" in your lock ordering
//! hierarchy, then implement [`LockLevel`] for each of them. Then carefully
//! implement [`relation::LockBefore`] to express the order in which pairs of
//! locks can be acquired. The [`relation::impl_transitive_lock_order`] macro
//! can help with this by providing transitive implementations of `LockBefore`.
//!
//! Next, you'll need to specify the acquisition method for each lock by
//! implementing one of [`crate::lock::MutexLock`], [`crate::lock::RwLock`], or
//! their `async` counterparts.
//!
//! Lastly, ensure that every call tree in your code constructs at most one
//! `LockedAt`. "Root" functions that are only called externally can construct
//! one using [`LockedAt::new`], but any non-root function that acquires a lock
//! should take a `&mut LockedAt<'_, L>` as an argument. Make sure you consider
//! the full call graph! A function invoked as a callback, for example, is not a
//! root function, and so should not construct a `LockedAt` unless the callback
//! invocation code doesn't acquire any locks.
//!
//! See the examples for more details.
pub use ;
/// The least-restrictive lock level, when no locks are held.
;
/// Marker for a type that indicates a level in the locking hierarchy.