| use super::CriticalSection; |
| use core::cell::{Ref, RefCell, RefMut, UnsafeCell}; |
| |
| /// A mutex based on critical sections. |
| /// |
| /// # Design |
| /// |
| /// [`std::sync::Mutex`] has two purposes. It converts types that are [`Send`] |
| /// but not [`Sync`] into types that are both; and it provides |
| /// [interior mutability]. `critical_section::Mutex`, on the other hand, only adds |
| /// `Sync`. It does *not* provide interior mutability. |
| /// |
| /// This was a conscious design choice. It is possible to create multiple |
| /// [`CriticalSection`] tokens, either by nesting critical sections or `Copy`ing |
| /// an existing token. As a result, it would not be sound for [`Mutex::borrow`] |
| /// to return `&mut T`, because there would be nothing to prevent calling |
| /// `borrow` multiple times to create aliased `&mut T` references. |
| /// |
| /// The solution is to include a runtime check to ensure that each resource is |
| /// borrowed only once. This is what `std::sync::Mutex` does. However, this is |
| /// a runtime cost that may not be required in all circumstances. For instance, |
| /// `Mutex<Cell<T>>` never needs to create `&mut T` or equivalent. |
| /// |
| /// If `&mut T` is needed, the simplest solution is to use `Mutex<RefCell<T>>`, |
| /// which is the closest analogy to `std::sync::Mutex`. [`RefCell`] inserts the |
| /// exact runtime check necessary to guarantee that the `&mut T` reference is |
| /// unique. |
| /// |
| /// To reduce verbosity when using `Mutex<RefCell<T>>`, we reimplement some of |
| /// `RefCell`'s methods on it directly. |
| /// |
| /// ``` |
| /// # use critical_section::{CriticalSection, Mutex}; |
| /// # use std::cell::RefCell; |
| /// |
| /// static FOO: Mutex<RefCell<i32>> = Mutex::new(RefCell::new(42)); |
| /// |
| /// fn main() { |
| /// let cs = unsafe { CriticalSection::new() }; |
| /// // Instead of calling this |
| /// let _ = FOO.borrow(cs).take(); |
| /// // Call this |
| /// let _ = FOO.take(cs); |
| /// // `RefCell::borrow` and `RefCell::borrow_mut` are renamed to |
| /// // `borrow_ref` and `borrow_ref_mut` to avoid name collisions |
| /// let _: &mut i32 = &mut *FOO.borrow_ref_mut(cs); |
| /// } |
| /// ``` |
| /// |
| /// [`std::sync::Mutex`]: https://doc.rust-lang.org/std/sync/struct.Mutex.html |
| /// [interior mutability]: https://doc.rust-lang.org/reference/interior-mutability.html |
| #[derive(Debug)] |
| pub struct Mutex<T> { |
| inner: UnsafeCell<T>, |
| } |
| |
| impl<T> Mutex<T> { |
| /// Creates a new mutex. |
| #[inline] |
| pub const fn new(value: T) -> Self { |
| Mutex { |
| inner: UnsafeCell::new(value), |
| } |
| } |
| |
| /// Gets a mutable reference to the contained value when the mutex is already uniquely borrowed. |
| /// |
| /// This does not require locking or a critical section since it takes `&mut self`, which |
| /// guarantees unique ownership already. Care must be taken when using this method to |
| /// **unsafely** access `static mut` variables, appropriate fences must be used to prevent |
| /// unwanted optimizations. |
| #[inline] |
| pub fn get_mut(&mut self) -> &mut T { |
| unsafe { &mut *self.inner.get() } |
| } |
| |
| /// Unwraps the contained value, consuming the mutex. |
| #[inline] |
| pub fn into_inner(self) -> T { |
| self.inner.into_inner() |
| } |
| |
| /// Borrows the data for the duration of the critical section. |
| #[inline] |
| pub fn borrow<'cs>(&'cs self, _cs: CriticalSection<'cs>) -> &'cs T { |
| unsafe { &*self.inner.get() } |
| } |
| } |
| |
| impl<T> Mutex<RefCell<T>> { |
| /// Borrow the data and call [`RefCell::replace`] |
| /// |
| /// This is equivalent to `self.borrow(cs).replace(t)` |
| /// |
| /// # Panics |
| /// |
| /// This call could panic. See the documentation for [`RefCell::replace`] |
| /// for more details. |
| #[inline] |
| #[track_caller] |
| pub fn replace<'cs>(&'cs self, cs: CriticalSection<'cs>, t: T) -> T { |
| self.borrow(cs).replace(t) |
| } |
| |
| /// Borrow the data and call [`RefCell::replace_with`] |
| /// |
| /// This is equivalent to `self.borrow(cs).replace_with(f)` |
| /// |
| /// # Panics |
| /// |
| /// This call could panic. See the documentation for |
| /// [`RefCell::replace_with`] for more details. |
| #[inline] |
| #[track_caller] |
| pub fn replace_with<'cs, F>(&'cs self, cs: CriticalSection<'cs>, f: F) -> T |
| where |
| F: FnOnce(&mut T) -> T, |
| { |
| self.borrow(cs).replace_with(f) |
| } |
| |
| /// Borrow the data and call [`RefCell::borrow`] |
| /// |
| /// This is equivalent to `self.borrow(cs).borrow()` |
| /// |
| /// # Panics |
| /// |
| /// This call could panic. See the documentation for [`RefCell::borrow`] |
| /// for more details. |
| #[inline] |
| #[track_caller] |
| pub fn borrow_ref<'cs>(&'cs self, cs: CriticalSection<'cs>) -> Ref<'cs, T> { |
| self.borrow(cs).borrow() |
| } |
| |
| /// Borrow the data and call [`RefCell::borrow_mut`] |
| /// |
| /// This is equivalent to `self.borrow(cs).borrow_mut()` |
| /// |
| /// # Panics |
| /// |
| /// This call could panic. See the documentation for [`RefCell::borrow_mut`] |
| /// for more details. |
| #[inline] |
| #[track_caller] |
| pub fn borrow_ref_mut<'cs>(&'cs self, cs: CriticalSection<'cs>) -> RefMut<'cs, T> { |
| self.borrow(cs).borrow_mut() |
| } |
| } |
| |
| impl<T: Default> Mutex<RefCell<T>> { |
| /// Borrow the data and call [`RefCell::take`] |
| /// |
| /// This is equivalent to `self.borrow(cs).take()` |
| /// |
| /// # Panics |
| /// |
| /// This call could panic. See the documentation for [`RefCell::take`] |
| /// for more details. |
| #[inline] |
| #[track_caller] |
| pub fn take<'cs>(&'cs self, cs: CriticalSection<'cs>) -> T { |
| self.borrow(cs).take() |
| } |
| } |
| |
| // NOTE A `Mutex` can be used as a channel so the protected data must be `Send` |
| // to prevent sending non-Sendable stuff (e.g. access tokens) across different |
| // threads. |
| unsafe impl<T> Sync for Mutex<T> where T: Send {} |
| |
| /// ``` compile_fail |
| /// fn bad(cs: critical_section::CriticalSection) -> &u32 { |
| /// let x = critical_section::Mutex::new(42u32); |
| /// x.borrow(cs) |
| /// } |
| /// ``` |
| #[cfg(doctest)] |
| const BorrowMustNotOutliveMutexTest: () = (); |