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}