1 #![cfg_attr(not(feature = "std"), no_std)]
2 #![doc = include_str!("../README.md")]
3
4 mod mutex;
5 #[cfg(feature = "std")]
6 mod std;
7
8 use core::marker::PhantomData;
9
10 pub use self::mutex::Mutex;
11
12 /// Critical section token.
13 ///
14 /// An instance of this type indicates that the current thread is executing code within a critical
15 /// section.
16 #[derive(Clone, Copy, Debug)]
17 pub struct CriticalSection<'cs> {
18 _private: PhantomData<&'cs ()>,
19
20 // Prevent CriticalSection from being Send or Sync
21 // https://github.com/rust-embedded/critical-section/issues/55
22 _not_send_sync: PhantomData<*mut ()>,
23 }
24
25 impl<'cs> CriticalSection<'cs> {
26 /// Creates a critical section token.
27 ///
28 /// This method is meant to be used to create safe abstractions rather than being directly used
29 /// in applications.
30 ///
31 /// # Safety
32 ///
33 /// This must only be called when the current thread is in a critical section. The caller must
34 /// ensure that the returned instance will not live beyond the end of the critical section.
35 ///
36 /// The caller must use adequate fences to prevent the compiler from moving the
37 /// instructions inside the critical section to the outside of it. Sequentially consistent fences are
38 /// suggested immediately after entry and immediately before exit from the critical section.
39 ///
40 /// Note that the lifetime `'cs` of the returned instance is unconstrained. User code must not
41 /// be able to influence the lifetime picked for this type, since that might cause it to be
42 /// inferred to `'static`.
43 #[inline(always)]
new() -> Self44 pub unsafe fn new() -> Self {
45 CriticalSection {
46 _private: PhantomData,
47 _not_send_sync: PhantomData,
48 }
49 }
50 }
51
52 #[cfg(any(
53 all(feature = "restore-state-none", feature = "restore-state-bool"),
54 all(feature = "restore-state-none", feature = "restore-state-u8"),
55 all(feature = "restore-state-none", feature = "restore-state-u16"),
56 all(feature = "restore-state-none", feature = "restore-state-u32"),
57 all(feature = "restore-state-none", feature = "restore-state-u64"),
58 all(feature = "restore-state-bool", feature = "restore-state-u8"),
59 all(feature = "restore-state-bool", feature = "restore-state-u16"),
60 all(feature = "restore-state-bool", feature = "restore-state-u32"),
61 all(feature = "restore-state-bool", feature = "restore-state-u64"),
62 all(feature = "restore-state-bool", feature = "restore-state-usize"),
63 all(feature = "restore-state-u8", feature = "restore-state-u16"),
64 all(feature = "restore-state-u8", feature = "restore-state-u32"),
65 all(feature = "restore-state-u8", feature = "restore-state-u64"),
66 all(feature = "restore-state-u8", feature = "restore-state-usize"),
67 all(feature = "restore-state-u16", feature = "restore-state-u32"),
68 all(feature = "restore-state-u16", feature = "restore-state-u64"),
69 all(feature = "restore-state-u16", feature = "restore-state-usize"),
70 all(feature = "restore-state-u32", feature = "restore-state-u64"),
71 all(feature = "restore-state-u32", feature = "restore-state-usize"),
72 all(feature = "restore-state-u64", feature = "restore-state-usize"),
73 ))]
74 compile_error!("You must set at most one of these Cargo features: restore-state-none, restore-state-bool, restore-state-u8, restore-state-u16, restore-state-u32, restore-state-u64, restore-state-usize");
75
76 #[cfg(not(any(
77 feature = "restore-state-bool",
78 feature = "restore-state-u8",
79 feature = "restore-state-u16",
80 feature = "restore-state-u32",
81 feature = "restore-state-u64",
82 feature = "restore-state-usize"
83 )))]
84 type RawRestoreStateInner = ();
85
86 #[cfg(feature = "restore-state-bool")]
87 type RawRestoreStateInner = bool;
88
89 #[cfg(feature = "restore-state-u8")]
90 type RawRestoreStateInner = u8;
91
92 #[cfg(feature = "restore-state-u16")]
93 type RawRestoreStateInner = u16;
94
95 #[cfg(feature = "restore-state-u32")]
96 type RawRestoreStateInner = u32;
97
98 #[cfg(feature = "restore-state-u64")]
99 type RawRestoreStateInner = u64;
100
101 #[cfg(feature = "restore-state-usize")]
102 type RawRestoreStateInner = usize;
103
104 // We have RawRestoreStateInner and RawRestoreState so that we don't have to copypaste the docs 5 times.
105 // In the docs this shows as `pub type RawRestoreState = u8` or whatever the selected type is, because
106 // the "inner" type alias is private.
107
108 /// Raw, transparent "restore state".
109 ///
110 /// This type changes based on which Cargo feature is selected, out of
111 /// - `restore-state-none` (default, makes the type be `()`)
112 /// - `restore-state-bool`
113 /// - `restore-state-u8`
114 /// - `restore-state-u16`
115 /// - `restore-state-u32`
116 /// - `restore-state-u64`
117 /// - `restore-state-usize`
118 ///
119 /// See [`RestoreState`].
120 ///
121 /// User code uses [`RestoreState`] opaquely, critical section implementations
122 /// use [`RawRestoreState`] so that they can use the inner value.
123 pub type RawRestoreState = RawRestoreStateInner;
124
125 /// Opaque "restore state".
126 ///
127 /// Implementations use this to "carry over" information between acquiring and releasing
128 /// a critical section. For example, when nesting two critical sections of an
129 /// implementation that disables interrupts globally, acquiring the inner one won't disable
130 /// the interrupts since they're already disabled. The impl would use the restore state to "tell"
131 /// the corresponding release that it does *not* have to reenable interrupts yet, only the
132 /// outer release should do so.
133 ///
134 /// User code uses [`RestoreState`] opaquely, critical section implementations
135 /// use [`RawRestoreState`] so that they can use the inner value.
136 #[derive(Clone, Copy, Debug)]
137 pub struct RestoreState(RawRestoreState);
138
139 impl RestoreState {
140 /// Create an invalid, dummy `RestoreState`.
141 ///
142 /// This can be useful to avoid `Option` when storing a `RestoreState` in a
143 /// struct field, or a `static`.
144 ///
145 /// Note that due to the safety contract of [`acquire`]/[`release`], you must not pass
146 /// a `RestoreState` obtained from this method to [`release`].
invalid() -> Self147 pub const fn invalid() -> Self {
148 #[cfg(not(any(
149 feature = "restore-state-bool",
150 feature = "restore-state-u8",
151 feature = "restore-state-u16",
152 feature = "restore-state-u32",
153 feature = "restore-state-u64",
154 feature = "restore-state-usize"
155 )))]
156 return Self(());
157
158 #[cfg(feature = "restore-state-bool")]
159 return Self(false);
160
161 #[cfg(feature = "restore-state-u8")]
162 return Self(0);
163
164 #[cfg(feature = "restore-state-u16")]
165 return Self(0);
166
167 #[cfg(feature = "restore-state-u32")]
168 return Self(0);
169
170 #[cfg(feature = "restore-state-u64")]
171 return Self(0);
172
173 #[cfg(feature = "restore-state-usize")]
174 return Self(0);
175 }
176 }
177
178 /// Acquire a critical section in the current thread.
179 ///
180 /// This function is extremely low level. Strongly prefer using [`with`] instead.
181 ///
182 /// Nesting critical sections is allowed. The inner critical sections
183 /// are mostly no-ops since they're already protected by the outer one.
184 ///
185 /// # Safety
186 ///
187 /// - Each `acquire` call must be paired with exactly one `release` call in the same thread.
188 /// - `acquire` returns a "restore state" that you must pass to the corresponding `release` call.
189 /// - `acquire`/`release` pairs must be "properly nested", ie it's not OK to do `a=acquire(); b=acquire(); release(a); release(b);`.
190 /// - It is UB to call `release` if the critical section is not acquired in the current thread.
191 /// - It is UB to call `release` with a "restore state" that does not come from the corresponding `acquire` call.
192 /// - It must provide ordering guarantees at least equivalent to a [`core::sync::atomic::Ordering::Acquire`]
193 /// on a memory location shared by all critical sections, on which the `release` call will do a
194 /// [`core::sync::atomic::Ordering::Release`] operation.
195 #[inline(always)]
acquire() -> RestoreState196 pub unsafe fn acquire() -> RestoreState {
197 extern "Rust" {
198 fn _critical_section_1_0_acquire() -> RawRestoreState;
199 }
200
201 #[allow(clippy::unit_arg)]
202 RestoreState(_critical_section_1_0_acquire())
203 }
204
205 /// Release the critical section.
206 ///
207 /// This function is extremely low level. Strongly prefer using [`with`] instead.
208 ///
209 /// # Safety
210 ///
211 /// See [`acquire`] for the safety contract description.
212 #[inline(always)]
release(restore_state: RestoreState)213 pub unsafe fn release(restore_state: RestoreState) {
214 extern "Rust" {
215 fn _critical_section_1_0_release(restore_state: RawRestoreState);
216 }
217
218 #[allow(clippy::unit_arg)]
219 _critical_section_1_0_release(restore_state.0)
220 }
221
222 /// Execute closure `f` in a critical section.
223 ///
224 /// Nesting critical sections is allowed. The inner critical sections
225 /// are mostly no-ops since they're already protected by the outer one.
226 ///
227 /// # Panics
228 ///
229 /// This function panics if the given closure `f` panics. In this case
230 /// the critical section is released before unwinding.
231 #[inline]
with<R>(f: impl FnOnce(CriticalSection) -> R) -> R232 pub fn with<R>(f: impl FnOnce(CriticalSection) -> R) -> R {
233 // Helper for making sure `release` is called even if `f` panics.
234 struct Guard {
235 state: RestoreState,
236 }
237
238 impl Drop for Guard {
239 #[inline(always)]
240 fn drop(&mut self) {
241 unsafe { release(self.state) }
242 }
243 }
244
245 let state = unsafe { acquire() };
246 let _guard = Guard { state };
247
248 unsafe { f(CriticalSection::new()) }
249 }
250
251 /// Methods required for a critical section implementation.
252 ///
253 /// This trait is not intended to be used except when implementing a critical section.
254 ///
255 /// # Safety
256 ///
257 /// Implementations must uphold the contract specified in [`crate::acquire`] and [`crate::release`].
258 pub unsafe trait Impl {
259 /// Acquire the critical section.
260 ///
261 /// # Safety
262 ///
263 /// Callers must uphold the contract specified in [`crate::acquire`] and [`crate::release`].
acquire() -> RawRestoreState264 unsafe fn acquire() -> RawRestoreState;
265
266 /// Release the critical section.
267 ///
268 /// # Safety
269 ///
270 /// Callers must uphold the contract specified in [`crate::acquire`] and [`crate::release`].
release(restore_state: RawRestoreState)271 unsafe fn release(restore_state: RawRestoreState);
272 }
273
274 /// Set the critical section implementation.
275 ///
276 /// # Example
277 ///
278 /// ```
279 /// # #[cfg(not(feature = "std"))] // needed for `cargo test --features std`
280 /// # mod no_std {
281 /// use critical_section::RawRestoreState;
282 ///
283 /// struct MyCriticalSection;
284 /// critical_section::set_impl!(MyCriticalSection);
285 ///
286 /// unsafe impl critical_section::Impl for MyCriticalSection {
287 /// unsafe fn acquire() -> RawRestoreState {
288 /// // ...
289 /// }
290 ///
291 /// unsafe fn release(restore_state: RawRestoreState) {
292 /// // ...
293 /// }
294 /// }
295 /// # }
296 #[macro_export]
297 macro_rules! set_impl {
298 ($t: ty) => {
299 #[no_mangle]
300 unsafe fn _critical_section_1_0_acquire() -> $crate::RawRestoreState {
301 <$t as $crate::Impl>::acquire()
302 }
303 #[no_mangle]
304 unsafe fn _critical_section_1_0_release(restore_state: $crate::RawRestoreState) {
305 <$t as $crate::Impl>::release(restore_state)
306 }
307 };
308 }
309