scoped_mutex/
lib.rs

1//! Scoped Mutex Crate
2#![deny(missing_docs)]
3#![cfg_attr(not(feature = "std"), no_std)]
4
5pub mod raw_impls;
6
7pub use scoped_mutex_traits::{ConstInit, ScopedRawMutex};
8use core::cell::UnsafeCell;
9
10/// Blocking mutex (not async)
11///
12/// Provides a blocking mutual exclusion primitive backed by an implementation of [`ScopedRawMutex`].
13///
14/// Which implementation you select depends on the context in which you're using the mutex, and you can choose which kind
15/// of interior mutability fits your use case.
16///
17/// Use [`CriticalSectionRawMutex`] when data can be shared between threads and interrupts.
18///
19/// Use [`LocalRawMutex`] when data is only shared between tasks running on the same executor.
20///
21/// Use [`ThreadModeRawMutex`] when data is shared between tasks running on the same executor but you want a global singleton.
22///
23/// In all cases, the blocking mutex is intended to be short lived and not held across await points.
24///
25/// [`CriticalSectionRawMutex`]: crate::raw_impls::cs::CriticalSectionRawMutex
26/// [`LocalRawMutex`]: crate::raw_impls::local::LocalRawMutex
27/// [`ThreadModeRawMutex`]: crate::raw_impls::single_core_thread_mode::ThreadModeRawMutex
28pub struct BlockingMutex<R, T: ?Sized> {
29    // NOTE: `raw` must be FIRST, so when using ThreadModeMutex the "can't drop in non-thread-mode" gets
30    // to run BEFORE dropping `data`.
31    raw: R,
32    data: UnsafeCell<T>,
33}
34
35unsafe impl<R: ScopedRawMutex + Send, T: ?Sized + Send> Send for BlockingMutex<R, T> {}
36unsafe impl<R: ScopedRawMutex + Sync, T: ?Sized + Send> Sync for BlockingMutex<R, T> {}
37
38impl<R: ConstInit, T> BlockingMutex<R, T> {
39    /// Creates a new mutex in an unlocked state ready for use.
40    #[inline]
41    pub const fn new(val: T) -> BlockingMutex<R, T> {
42        BlockingMutex {
43            raw: R::INIT,
44            data: UnsafeCell::new(val),
45        }
46    }
47}
48
49impl<R: ScopedRawMutex, T> BlockingMutex<R, T> {
50    /// Locks the raw mutex and grants temporary access to the inner data
51    ///
52    /// Behavior when the lock is already locked is dependent on the behavior
53    /// of the Raw mutex. See [`ScopedRawMutex::with_lock()`]'s documentation for
54    /// more details
55    pub fn with_lock<U>(&self, f: impl FnOnce(&mut T) -> U) -> U {
56        self.raw.with_lock(|| {
57            let ptr = self.data.get();
58            // SAFETY: Raw Mutex proves we have exclusive access to the inner data
59            let inner = unsafe { &mut *ptr };
60            f(inner)
61        })
62    }
63
64    /// Locks the raw mutex and grants temporary access to the inner data
65    ///
66    /// Returns `Some(U)` if the lock was obtained. Returns `None` if the lock
67    /// was already locked
68    #[must_use]
69    pub fn try_with_lock<U>(&self, f: impl FnOnce(&mut T) -> U) -> Option<U> {
70        self.raw.try_with_lock(|| {
71            let ptr = self.data.get();
72            // SAFETY: Raw Mutex proves we have exclusive access to the inner data
73            let inner = unsafe { &mut *ptr };
74            f(inner)
75        })
76    }
77}
78
79impl<R, T> BlockingMutex<R, T> {
80    /// Creates a new mutex based on a pre-existing raw mutex.
81    ///
82    /// This allows creating a mutex in a constant context on stable Rust.
83    #[inline]
84    pub const fn const_new(raw_mutex: R, val: T) -> BlockingMutex<R, T> {
85        BlockingMutex {
86            raw: raw_mutex,
87            data: UnsafeCell::new(val),
88        }
89    }
90
91    /// Consumes this mutex, returning the underlying data.
92    #[inline]
93    pub fn into_inner(self) -> T {
94        self.data.into_inner()
95    }
96
97    /// Returns a mutable reference to the underlying data.
98    ///
99    /// Since this call borrows the `Mutex` mutably, no actual locking needs to
100    /// take place---the mutable borrow statically guarantees no locks exist.
101    #[inline]
102    pub fn get_mut(&mut self) -> &mut T {
103        unsafe { &mut *self.data.get() }
104    }
105
106    /// Returns a pointer to the inner storage
107    ///
108    /// # Safety
109    ///
110    /// Must NOT be called when the lock is taken
111    pub unsafe fn get_unchecked(&self) -> *mut T {
112        self.data.get()
113    }
114}