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