ordered_locks/lib.rs
1//! This create implement compiletime ordering of locks into levels, [`L1`], [`L2`], [`L3`], [`L4`] and [`L5`].
2//! In order to acquire a lock at level `i` only locks at level `i-1` or below may be held.
3//!
4//! If locks are alwayes acquired in level order on all threads, then one cannot have a deadlock
5//! involving only acquireng locks.
6//!
7//! In the following example we create two [muteces](Mutex) at level [`L1`] and [`L2`] and lock them
8//! in the propper order.
9//! ```
10//! use ordered_locks::{L1, L2, Mutex, CleanLockToken};
11//! // Create value at lock level 0, this lock cannot be acquired while a level1 lock is heldt
12//! let v1 = Mutex::<L1, _>::new(42);
13//! // Create value at lock level 1
14//! let v2 = Mutex::<L2, _>::new(43);
15//! // Construct a token indicating that this thread does not hold any locks
16//! let mut token = unsafe {CleanLockToken::new()};
17//!
18//! {
19//! // We can aquire the locks for v1 and v2 at the same time
20//! let mut g1 = v1.lock(token.token());
21//! let (g1, token) = g1.token_split();
22//! let mut g2 = v2.lock(token);
23//! *g2 = 11;
24//! *g1 = 12;
25//! }
26//! // Once the guards are dropped we can acquire other things
27//! *v2.lock(token.token()) = 13;
28//! ```
29//!
30//! In the following example we create two [muteces](Mutex) at level [`L1`] and [`L2`] and try to lock
31//! the mutex at [`L1`] while already holding a [`Mutex`] at [`L2`] which failes to compile.
32//! ```compile_fail
33//! use ordered_locks::{L1, L2, Mutex, CleanLockToken};
34//! // Create value at lock level 0, this lock cannot be acquired while a level1 lock is heldt
35//! let v1 = Mutex::<L1, _>::new(42);
36//! // Create value at lock level 1
37//! let v2 = Mutex::<L2, _>::new(43);
38//! // Construct a token indicating that this thread does not hold any locks
39//! let mut clean_token = unsafe {CleanLockToken::new()};
40//! let token = clean_token.token();
41//!
42//! // Try to aquire locks in the wrong order
43//! let mut g2 = v2.lock(token);
44//! let (g2, token) = g2.token_split();
45//! let mut g1 = v1.lock(token); // shouldn't compile!
46//! *g2 = 11;
47//! *g1 = 12;
48//! ```
49use std::marker::PhantomData;
50
51/// Lock level of a mutex
52///
53/// While a mutex of L1 is locked on a thread, only mutexes of L2 or higher may be locked.
54/// This lock hierarchy prevents deadlocks from occurring. For a dead lock to occour
55/// We need some thread TA to hold a resource RA, and request a resource RB, while
56/// another thread TB holds RB, and requests RA. This is not possible with a lock
57/// hierarchy either RA or RB must be on a level that the other.
58///
59/// At some point in time we would want Level to be replaced by usize, however
60/// with current cont generics (rust 1.55), we cannot compare const generic arguments
61/// so we are left with this mess.
62pub trait Level {}
63
64/// Indicate that the implementor is lower that the level O
65pub trait Lower<O: Level>: Level {}
66
67/// Lowest locking level, no locks can be on this level
68#[derive(Debug)]
69pub struct L0 {}
70
71#[derive(Debug)]
72pub struct L1 {}
73
74#[derive(Debug)]
75pub struct L2 {}
76
77#[derive(Debug)]
78pub struct L3 {}
79
80#[derive(Debug)]
81pub struct L4 {}
82
83#[derive(Debug)]
84pub struct L5 {}
85
86impl Level for L0 {}
87impl Level for L1 {}
88impl Level for L2 {}
89impl Level for L3 {}
90impl Level for L4 {}
91impl Level for L5 {}
92
93impl Lower<L1> for L0 {}
94impl Lower<L2> for L0 {}
95impl Lower<L3> for L0 {}
96impl Lower<L4> for L0 {}
97impl Lower<L5> for L0 {}
98
99impl Lower<L2> for L1 {}
100impl Lower<L3> for L1 {}
101impl Lower<L4> for L1 {}
102impl Lower<L5> for L1 {}
103
104impl Lower<L3> for L2 {}
105impl Lower<L4> for L2 {}
106impl Lower<L5> for L2 {}
107
108impl Lower<L4> for L3 {}
109impl Lower<L5> for L3 {}
110
111impl Lower<L5> for L4 {}
112
113/// Indicate that the implementor is higher that the level O
114pub trait Higher<O: Level>: Level {}
115impl<L1: Level, L2: Level> Higher<L2> for L1 where L2: Lower<L1> {}
116
117/// While this exists only locks with a level higher than L, may be locked.
118/// These tokens are carried around the call stack to indicate tho current locking level.
119/// They have no size and should disappear at runtime.
120pub struct LockToken<'a, L: Level>(PhantomData<&'a mut L>);
121
122impl<'a, L: Level> LockToken<'a, L> {
123 /// Create a borrowed copy of self
124 pub fn token(&mut self) -> LockToken<'_, L> {
125 LockToken(Default::default())
126 }
127
128 /// Create a borrowed copy of self, on a higher level
129 pub fn downgrade<LC: Higher<L>>(&mut self) -> LockToken<'_, LC> {
130 LockToken(Default::default())
131 }
132
133 pub fn downgraded<LP: Lower<L>>(_: LockToken<'a, LP>) -> Self {
134 LockToken(Default::default())
135 }
136}
137
138/// Token indicating that there are no acquired locks while not borrowed.
139pub struct CleanLockToken;
140
141impl CleanLockToken {
142 /// Create a borrowed copy of self
143 pub fn token(&mut self) -> LockToken<'_, L0> {
144 LockToken(Default::default())
145 }
146
147 /// Create a borrowed copy of self, on a higher level
148 pub fn downgrade<L: Level>(&mut self) -> LockToken<'_, L> {
149 LockToken(Default::default())
150 }
151
152 /// Create a new instance
153 ///
154 /// # Safety
155 ///
156 /// This is safe to call as long as there are no currently acquired locks
157 /// in the thread/task, and as long as there are no other CleanLockToken
158 /// in the thread/task.
159 ///
160 /// A CleanLockToken
161 pub unsafe fn new() -> Self {
162 CleanLockToken
163 }
164}
165
166/// A mutual exclusion primitive useful for protecting shared data
167///
168/// This mutex will block threads waiting for the lock to become available. The
169/// mutex can also be statically initialized or created via a `new`
170/// constructor. Each mutex has a type parameter which represents the data that
171/// it is protecting. The data can only be accessed through the RAII guards
172/// returned from `lock` and `try_lock`, which guarantees that the data is only
173/// ever accessed when the mutex is locked.
174#[derive(Debug)]
175pub struct Mutex<L: Level, T> {
176 inner: std::sync::Mutex<T>,
177 _phantom: PhantomData<L>,
178}
179
180impl<L: Level, T: Default> Default for Mutex<L, T> {
181 fn default() -> Self {
182 Self {
183 inner: Default::default(),
184 _phantom: Default::default(),
185 }
186 }
187}
188
189impl<L: Level, T> Mutex<L, T> {
190 /// Creates a new mutex in an unlocked state ready for use
191 pub fn new(val: T) -> Self {
192 Self {
193 inner: std::sync::Mutex::new(val),
194 _phantom: Default::default(),
195 }
196 }
197
198 /// Acquires a mutex, blocking the current thread until it is able to do so.
199 ///
200 /// This function will block the local thread until it is available to acquire the mutex.
201 /// Upon returning, the thread is the only thread with the mutex held.
202 /// An RAII guard is returned to allow scoped unlock of the lock. When the guard goes out of scope, the mutex will be unlocked.
203 pub fn lock<'a, LP: Lower<L> + 'a>(
204 &'a self,
205 lock_token: LockToken<'a, LP>,
206 ) -> MutexGuard<'a, L, T> {
207 MutexGuard {
208 inner: self.inner.lock().unwrap(),
209 lock_token: LockToken::downgraded(lock_token),
210 }
211 }
212
213 /// Attempts to acquire this lock.
214 ///
215 /// If the lock could not be acquired at this time, then `None` is returned.
216 /// Otherwise, an RAII guard is returned. The lock will be unlocked when the
217 /// guard is dropped.
218 ///
219 /// This function does not block.
220 pub fn try_lock<'a, LP: Lower<L> + 'a>(
221 &'a self,
222 lock_token: LockToken<'a, LP>,
223 ) -> Option<MutexGuard<'a, L, T>> {
224 match self.inner.try_lock() {
225 Ok(inner) => Some(MutexGuard {
226 inner,
227 lock_token: LockToken::downgraded(lock_token),
228 }),
229 Err(std::sync::TryLockError::Poisoned(_)) => panic!("Poised lock"),
230 Err(std::sync::TryLockError::WouldBlock) => None,
231 }
232 }
233
234 /// Attempts to acquire this lock. Timeout after duration has expired
235 ///
236 /// Since this operation is not supported by a std::sync::mutex this
237 /// this is implemented using try_lock and waits
238 pub fn try_lock_for<'a, LP: Lower<L> + 'a>(
239 &'a self,
240 lock_token: LockToken<'a, LP>,
241 duration: std::time::Duration,
242 ) -> Option<MutexGuard<'a, L, T>> {
243 let try_counts = 16;
244 for _ in 0..try_counts {
245 if let Some(lock) = self.try_lock(LockToken::<LP>(Default::default())) {
246 return Some(lock);
247 }
248 std::thread::sleep(duration / try_counts)
249 }
250 self.try_lock(lock_token)
251 }
252
253 /// Consumes this Mutex, returning the underlying data.
254 pub fn into_inner(self) -> T {
255 self.inner.into_inner().unwrap()
256 }
257}
258
259/// An RAII implementation of a "scoped lock" of a mutex. When this structure is
260/// dropped (falls out of scope), the lock will be unlocked.
261///
262/// The data protected by the mutex can be accessed through this guard via its
263/// `Deref` and `DerefMut` implementations.
264pub struct MutexGuard<'a, L: Level, T: ?Sized + 'a> {
265 inner: std::sync::MutexGuard<'a, T>,
266 lock_token: LockToken<'a, L>,
267}
268
269impl<'a, L: Level, T: ?Sized + 'a> MutexGuard<'a, L, T> {
270 /// Split the guard into two parts, the first a mutable reference to the held content
271 /// the second a [`LockToken`] that can be used for further locking
272 pub fn token_split(&mut self) -> (&mut T, LockToken<'_, L>) {
273 (&mut self.inner, self.lock_token.token())
274 }
275}
276
277impl<'a, L: Level, T: ?Sized + 'a> std::ops::Deref for MutexGuard<'a, L, T> {
278 type Target = T;
279
280 fn deref(&self) -> &Self::Target {
281 self.inner.deref()
282 }
283}
284impl<'a, L: Level, T: ?Sized + 'a> std::ops::DerefMut for MutexGuard<'a, L, T> {
285 fn deref_mut(&mut self) -> &mut Self::Target {
286 self.inner.deref_mut()
287 }
288}
289
290pub struct RwLock<L: Level, T> {
291 inner: std::sync::RwLock<T>,
292 _phantom: PhantomData<L>,
293}
294
295impl<L: Level, T: Default> Default for RwLock<L, T> {
296 fn default() -> Self {
297 Self {
298 inner: Default::default(),
299 _phantom: Default::default(),
300 }
301 }
302}
303
304/// A reader-writer lock
305///
306/// This type of lock allows a number of readers or at most one writer at any point in time.
307/// The write portion of this lock typically allows modification of the underlying data (exclusive access)
308/// and the read portion of this lock typically allows for read-only access (shared access).
309///
310/// The type parameter T represents the data that this lock protects. It is required that T satisfies
311/// Send to be shared across threads and Sync to allow concurrent access through readers.
312/// The RAII guards returned from the locking methods implement Deref (and DerefMut for the write methods)
313/// to allow access to the contained of the lock.
314impl<L: Level, T> RwLock<L, T> {
315 /// Creates a new instance of an RwLock<T> which is unlocked.
316 pub fn new(val: T) -> Self {
317 Self {
318 inner: std::sync::RwLock::new(val),
319 _phantom: Default::default(),
320 }
321 }
322
323 /// Consumes this RwLock, returning the underlying data.
324 pub fn into_inner(self) -> T {
325 self.inner.into_inner().unwrap()
326 }
327
328 /// Locks this RwLock with exclusive write access, blocking the current thread until it can be acquired.
329 /// This function will not return while other writers or other readers currently have access to the lock.
330 /// Returns an RAII guard which will drop the write access of this RwLock when dropped.
331 pub fn write<'a, LP: Lower<L> + 'a>(
332 &'a self,
333 lock_token: LockToken<'a, LP>,
334 ) -> RwLockWriteGuard<'a, L, T> {
335 RwLockWriteGuard {
336 inner: self.inner.write().unwrap(),
337 lock_token: LockToken::downgraded(lock_token),
338 }
339 }
340
341 /// Locks this RwLock with shared read access, blocking the current thread until it can be acquired.
342 ///
343 /// The calling thread will be blocked until there are no more writers which hold the lock.
344 /// There may be other readers currently inside the lock when this method returns.
345 ///
346 /// Note that attempts to recursively acquire a read lock on a RwLock when the current thread
347 /// already holds one may result in a deadlock.
348 ///
349 /// Returns an RAII guard which will release this thread’s shared access once it is dropped.
350 pub fn read<'a, LP: Lower<L> + 'a>(
351 &'a self,
352 lock_token: LockToken<'a, LP>,
353 ) -> RwLockReadGuard<'a, L, T> {
354 RwLockReadGuard {
355 inner: self.inner.read().unwrap(),
356 lock_token: LockToken::downgraded(lock_token),
357 }
358 }
359}
360
361/// RAII structure used to release the exclusive write access of a lock when dropped
362pub struct RwLockWriteGuard<'a, L: Level, T> {
363 inner: std::sync::RwLockWriteGuard<'a, T>,
364 lock_token: LockToken<'a, L>,
365}
366
367impl<'a, L: Level, T> RwLockWriteGuard<'a, L, T> {
368 /// Split the guard into two parts, the first a mutable reference to the held content
369 /// the second a [`LockToken`] that can be used for further locking
370 pub fn token_split(&mut self) -> (&mut T, LockToken<'_, L>) {
371 (&mut self.inner, self.lock_token.token())
372 }
373}
374
375impl<'a, L: Level, T> std::ops::Deref for RwLockWriteGuard<'a, L, T> {
376 type Target = T;
377
378 fn deref(&self) -> &Self::Target {
379 self.inner.deref()
380 }
381}
382
383impl<'a, L: Level, T> std::ops::DerefMut for RwLockWriteGuard<'a, L, T> {
384 fn deref_mut(&mut self) -> &mut Self::Target {
385 self.inner.deref_mut()
386 }
387}
388
389/// RAII structure used to release the shared read access of a lock when dropped.
390pub struct RwLockReadGuard<'a, L: Level, T> {
391 inner: std::sync::RwLockReadGuard<'a, T>,
392 lock_token: LockToken<'a, L>,
393}
394
395impl<'a, L: Level, T> RwLockReadGuard<'a, L, T> {
396 /// Split the guard into two parts, the first a reference to the held content
397 /// the second a [`LockToken`] that can be used for further locking
398 pub fn token_split(&mut self) -> (&T, LockToken<'_, L>) {
399 (&self.inner, self.lock_token.token())
400 }
401}
402
403impl<'a, L: Level, T> std::ops::Deref for RwLockReadGuard<'a, L, T> {
404 type Target = T;
405
406 fn deref(&self) -> &Self::Target {
407 self.inner.deref()
408 }
409}
410
411/// An asynchronous `Mutex`-like type.
412///
413/// This type acts similarly to [`std::sync::Mutex`], with two major
414/// differences: `lock` is an async method so does not block, and the lock
415/// guard is designed to be held across `.await` points.
416#[cfg(feature = "tokio")]
417#[derive(Debug)]
418pub struct AsyncMutex<L: Level, T> {
419 inner: tokio::sync::Mutex<T>,
420 _phantom: PhantomData<L>,
421}
422
423#[cfg(feature = "tokio")]
424impl<L: Level, T> AsyncMutex<L, T> {
425 /// Creates a new lock in an unlocked state ready for use.
426 pub fn new(val: T) -> Self {
427 Self {
428 inner: tokio::sync::Mutex::new(val),
429 _phantom: Default::default(),
430 }
431 }
432
433 // Locks this mutex, causing the current task to yield until the lock has been acquired.
434 // When the lock has been acquired, function returns a MutexGuard
435 pub async fn lock<'a, LP: Lower<L> + 'a>(
436 &'a self,
437 lock_token: LockToken<'a, LP>,
438 ) -> AsyncMutexGuard<'a, L, T> {
439 AsyncMutexGuard {
440 inner: self.inner.lock().await,
441 lock_token: LockToken::downgraded(lock_token),
442 }
443 }
444
445 /// Attempts to acquire the lock, and returns TryLockError if the lock is currently held somewhere else.
446 ///
447 /// The `lock_token` is held until the lock is released, to get a token for further locking
448 /// call [`AsyncMutexGuard::token_split`]
449 pub fn try_lock<'a, LP: Lower<L> + 'a>(
450 &'a self,
451 lock_token: LockToken<'a, LP>,
452 ) -> Result<AsyncMutexGuard<'a, L, T>, tokio::sync::TryLockError> {
453 self.inner.try_lock().map(|inner| AsyncMutexGuard {
454 inner,
455 lock_token: LockToken::downgraded(lock_token),
456 })
457 }
458
459 /// Consumes this Mutex, returning the underlying data.
460 pub fn into_inner(self) -> T {
461 self.inner.into_inner()
462 }
463}
464
465#[cfg(feature = "tokio")]
466impl<L: Level, T: Default> Default for AsyncMutex<L, T> {
467 fn default() -> Self {
468 Self {
469 inner: Default::default(),
470 _phantom: Default::default(),
471 }
472 }
473}
474
475/// A handle to a held `Mutex`. The guard can be held across any `.await` point
476/// as it is [`Send`].
477///
478/// As long as you have this guard, you have exclusive access to the underlying
479/// `T`. The guard internally borrows the `Mutex`, so the mutex will not be
480/// dropped while a guard exists.
481///
482/// The lock is automatically released whenever the guard is dropped, at which
483/// point `lock` will succeed yet again.
484#[cfg(feature = "tokio")]
485pub struct AsyncMutexGuard<'a, L: Level, T: ?Sized + 'a> {
486 inner: tokio::sync::MutexGuard<'a, T>,
487 lock_token: LockToken<'a, L>,
488}
489
490#[cfg(feature = "tokio")]
491impl<'a, L: Level, T: ?Sized + 'a> AsyncMutexGuard<'a, L, T> {
492 /// Split the guard into two parts, the first a mutable reference to the held content
493 /// the second a [`LockToken`] that can be used for further locking
494 pub fn token_split(&mut self) -> (&mut T, LockToken<'_, L>) {
495 (&mut self.inner, self.lock_token.token())
496 }
497}
498
499#[cfg(feature = "tokio")]
500impl<'a, L: Level, T: ?Sized + 'a> std::ops::Deref for AsyncMutexGuard<'a, L, T> {
501 type Target = T;
502
503 fn deref(&self) -> &Self::Target {
504 self.inner.deref()
505 }
506}
507#[cfg(feature = "tokio")]
508impl<'a, L: Level, T: ?Sized + 'a> std::ops::DerefMut for AsyncMutexGuard<'a, L, T> {
509 fn deref_mut(&mut self) -> &mut Self::Target {
510 self.inner.deref_mut()
511 }
512}
513
514/// This function can only be called if no lock is held by the calling thread/task
515#[inline]
516pub fn check_no_locks(_: LockToken<'_, L0>) {}