embassy_sync/blocking_mutex/raw.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
//! Mutex primitives.
//!
//! This module provides a trait for mutexes that can be used in different contexts.
use core::marker::PhantomData;
/// Raw mutex trait.
///
/// This mutex is "raw", which means it does not actually contain the protected data, it
/// just implements the mutex mechanism. For most uses you should use [`super::Mutex`] instead,
/// which is generic over a RawMutex and contains the protected data.
///
/// Note that, unlike other mutexes, implementations only guarantee no
/// concurrent access from other threads: concurrent access from the current
/// thread is allowed. For example, it's possible to lock the same mutex multiple times reentrantly.
///
/// Therefore, locking a `RawMutex` is only enough to guarantee safe shared (`&`) access
/// to the data, it is not enough to guarantee exclusive (`&mut`) access.
///
/// # Safety
///
/// RawMutex implementations must ensure that, while locked, no other thread can lock
/// the RawMutex concurrently.
///
/// Unsafe code is allowed to rely on this fact, so incorrect implementations will cause undefined behavior.
pub unsafe trait RawMutex {
/// Create a new `RawMutex` instance.
///
/// This is a const instead of a method to allow creating instances in const context.
const INIT: Self;
/// Lock this `RawMutex`.
fn lock<R>(&self, f: impl FnOnce() -> R) -> R;
}
/// A mutex that allows borrowing data across executors and interrupts.
///
/// # Safety
///
/// This mutex is safe to share between different executors and interrupts.
pub struct CriticalSectionRawMutex {
_phantom: PhantomData<()>,
}
unsafe impl Send for CriticalSectionRawMutex {}
unsafe impl Sync for CriticalSectionRawMutex {}
impl CriticalSectionRawMutex {
/// Create a new `CriticalSectionRawMutex`.
pub const fn new() -> Self {
Self { _phantom: PhantomData }
}
}
unsafe impl RawMutex for CriticalSectionRawMutex {
const INIT: Self = Self::new();
fn lock<R>(&self, f: impl FnOnce() -> R) -> R {
critical_section::with(|_| f())
}
}
// ================
/// A mutex that allows borrowing data in the context of a single executor.
///
/// # Safety
///
/// **This Mutex is only safe within a single executor.**
pub struct NoopRawMutex {
_phantom: PhantomData<*mut ()>,
}
unsafe impl Send for NoopRawMutex {}
impl NoopRawMutex {
/// Create a new `NoopRawMutex`.
pub const fn new() -> Self {
Self { _phantom: PhantomData }
}
}
unsafe impl RawMutex for NoopRawMutex {
const INIT: Self = Self::new();
fn lock<R>(&self, f: impl FnOnce() -> R) -> R {
f()
}
}
// ================
#[cfg(any(cortex_m, feature = "std"))]
mod thread_mode {
use super::*;
/// A "mutex" that only allows borrowing from thread mode.
///
/// # Safety
///
/// **This Mutex is only safe on single-core systems.**
///
/// On multi-core systems, a `ThreadModeRawMutex` **is not sufficient** to ensure exclusive access.
pub struct ThreadModeRawMutex {
_phantom: PhantomData<()>,
}
unsafe impl Send for ThreadModeRawMutex {}
unsafe impl Sync for ThreadModeRawMutex {}
impl ThreadModeRawMutex {
/// Create a new `ThreadModeRawMutex`.
pub const fn new() -> Self {
Self { _phantom: PhantomData }
}
}
unsafe impl RawMutex for ThreadModeRawMutex {
const INIT: Self = Self::new();
fn lock<R>(&self, f: impl FnOnce() -> R) -> R {
assert!(in_thread_mode(), "ThreadModeMutex can only be locked from thread mode.");
f()
}
}
impl Drop for ThreadModeRawMutex {
fn drop(&mut self) {
// Only allow dropping from thread mode. Dropping calls drop on the inner `T`, so
// `drop` needs the same guarantees as `lock`. `ThreadModeMutex<T>` is Send even if
// T isn't, so without this check a user could create a ThreadModeMutex in thread mode,
// send it to interrupt context and drop it there, which would "send" a T even if T is not Send.
assert!(
in_thread_mode(),
"ThreadModeMutex can only be dropped from thread mode."
);
// Drop of the inner `T` happens after this.
}
}
pub(crate) fn in_thread_mode() -> bool {
#[cfg(feature = "std")]
return Some("main") == std::thread::current().name();
#[cfg(not(feature = "std"))]
// ICSR.VECTACTIVE == 0
return unsafe { (0xE000ED04 as *const u32).read_volatile() } & 0x1FF == 0;
}
}
#[cfg(any(cortex_m, feature = "std"))]
pub use thread_mode::*;