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>) {}