• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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