aboutsummaryrefslogtreecommitdiff
path: root/embassy-sync/src/blocking_rwlock/raw.rs
diff options
context:
space:
mode:
authorAlix ANNERAUD <[email protected]>2025-02-28 16:28:10 +0100
committerAlix ANNERAUD <[email protected]>2025-02-28 16:28:10 +0100
commita7ecf14259591ff5b324ec1c7d7c521fabebe7d3 (patch)
treea0222745b1cf91756a6c3c8d2da9e217ecc9bec2 /embassy-sync/src/blocking_rwlock/raw.rs
parent114cfdd86b19640ca109488fb960020de673ec57 (diff)
Add blocking read-write lock implementation and remove obsolete tests
Diffstat (limited to 'embassy-sync/src/blocking_rwlock/raw.rs')
-rw-r--r--embassy-sync/src/blocking_rwlock/raw.rs159
1 files changed, 159 insertions, 0 deletions
diff --git a/embassy-sync/src/blocking_rwlock/raw.rs b/embassy-sync/src/blocking_rwlock/raw.rs
new file mode 100644
index 000000000..85e8374b5
--- /dev/null
+++ b/embassy-sync/src/blocking_rwlock/raw.rs
@@ -0,0 +1,159 @@
1//! Read-Write Lock primitives.
2//!
3//! This module provides a trait for read-write locks that can be used in different contexts.
4use core::marker::PhantomData;
5
6/// Raw read-write lock trait.
7///
8/// This read-write lock is "raw", which means it does not actually contain the protected data, it
9/// just implements the read-write lock mechanism. For most uses you should use [`super::RwLock`] instead,
10/// which is generic over a RawRwLock and contains the protected data.
11///
12/// Note that, unlike other read-write locks, implementations only guarantee no
13/// concurrent access from other threads: concurrent access from the current
14/// thread is allowed. For example, it's possible to lock the same read-write lock multiple times reentrantly.
15///
16/// Therefore, locking a `RawRwLock` is only enough to guarantee safe shared (`&`) access
17/// to the data, it is not enough to guarantee exclusive (`&mut`) access.
18///
19/// # Safety
20///
21/// RawRwLock implementations must ensure that, while locked, no other thread can lock
22/// the RawRwLock concurrently.
23///
24/// Unsafe code is allowed to rely on this fact, so incorrect implementations will cause undefined behavior.
25pub unsafe trait RawRwLock {
26 /// Create a new `RawRwLock` instance.
27 ///
28 /// This is a const instead of a method to allow creating instances in const context.
29 const INIT: Self;
30
31 /// Lock this `RawRwLock` for reading.
32 fn read_lock<R>(&self, f: impl FnOnce() -> R) -> R;
33
34 /// Lock this `RawRwLock` for writing.
35 fn write_lock<R>(&self, f: impl FnOnce() -> R) -> R;
36}
37
38/// A read-write lock that allows borrowing data across executors and interrupts.
39///
40/// # Safety
41///
42/// This read-write lock is safe to share between different executors and interrupts.
43pub struct CriticalSectionRawRwLock {
44 _phantom: PhantomData<()>,
45}
46unsafe impl Send for CriticalSectionRawRwLock {}
47unsafe impl Sync for CriticalSectionRawRwLock {}
48
49impl CriticalSectionRawRwLock {
50 /// Create a new `CriticalSectionRawRwLock`.
51 pub const fn new() -> Self {
52 Self { _phantom: PhantomData }
53 }
54}
55
56unsafe impl RawRwLock for CriticalSectionRawRwLock {
57 const INIT: Self = Self::new();
58
59 fn read_lock<R>(&self, f: impl FnOnce() -> R) -> R {
60 critical_section::with(|_| f())
61 }
62
63 fn write_lock<R>(&self, f: impl FnOnce() -> R) -> R {
64 critical_section::with(|_| f())
65 }
66}
67
68// ================
69
70/// A read-write lock that allows borrowing data in the context of a single executor.
71///
72/// # Safety
73///
74/// **This Read-Write Lock is only safe within a single executor.**
75pub struct NoopRawRwLock {
76 _phantom: PhantomData<*mut ()>,
77}
78
79unsafe impl Send for NoopRawRwLock {}
80
81impl NoopRawRwLock {
82 /// Create a new `NoopRawRwLock`.
83 pub const fn new() -> Self {
84 Self { _phantom: PhantomData }
85 }
86}
87
88unsafe impl RawRwLock for NoopRawRwLock {
89 const INIT: Self = Self::new();
90 fn read_lock<R>(&self, f: impl FnOnce() -> R) -> R {
91 f()
92 }
93
94 fn write_lock<R>(&self, f: impl FnOnce() -> R) -> R {
95 f()
96 }
97}
98
99// ================
100
101#[cfg(any(cortex_m, feature = "std"))]
102mod thread_mode {
103 use super::*;
104
105 /// A "read-write lock" that only allows borrowing from thread mode.
106 ///
107 /// # Safety
108 ///
109 /// **This Read-Write Lock is only safe on single-core systems.**
110 ///
111 /// On multi-core systems, a `ThreadModeRawRwLock` **is not sufficient** to ensure exclusive access.
112 pub struct ThreadModeRawRwLock {
113 _phantom: PhantomData<()>,
114 }
115
116 unsafe impl Send for ThreadModeRawRwLock {}
117 unsafe impl Sync for ThreadModeRawRwLock {}
118
119 impl ThreadModeRawRwLock {
120 /// Create a new `ThreadModeRawRwLock`.
121 pub const fn new() -> Self {
122 Self { _phantom: PhantomData }
123 }
124 }
125
126 unsafe impl RawRwLock for ThreadModeRawRwLock {
127 const INIT: Self = Self::new();
128 fn read_lock<R>(&self, f: impl FnOnce() -> R) -> R {
129 assert!(in_thread_mode(), "ThreadModeRwLock can only be locked from thread mode.");
130
131 f()
132 }
133
134 fn write_lock<R>(&self, f: impl FnOnce() -> R) -> R {
135 assert!(in_thread_mode(), "ThreadModeRwLock can only be locked from thread mode.");
136
137 f()
138 }
139 }
140
141 impl Drop for ThreadModeRawRwLock {
142 fn drop(&mut self) {
143 assert!(
144 in_thread_mode(),
145 "ThreadModeRwLock can only be dropped from thread mode."
146 );
147 }
148 }
149
150 pub(crate) fn in_thread_mode() -> bool {
151 #[cfg(feature = "std")]
152 return Some("main") == std::thread::current().name();
153
154 #[cfg(not(feature = "std"))]
155 return unsafe { (0xE000ED04 as *const u32).read_volatile() } & 0x1FF == 0;
156 }
157}
158#[cfg(any(cortex_m, feature = "std"))]
159pub use thread_mode::*; \ No newline at end of file