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 | |
21 | impl<'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 | ))] |
64 | 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" ); |
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 | )))] |
73 | type RawRestoreStateInner = (); |
74 | |
75 | #[cfg (feature = "restore-state-bool" )] |
76 | type RawRestoreStateInner = bool; |
77 | |
78 | #[cfg (feature = "restore-state-u8" )] |
79 | type RawRestoreStateInner = u8; |
80 | |
81 | #[cfg (feature = "restore-state-u16" )] |
82 | type RawRestoreStateInner = u16; |
83 | |
84 | #[cfg (feature = "restore-state-u32" )] |
85 | type RawRestoreStateInner = u32; |
86 | |
87 | #[cfg (feature = "restore-state-u64" )] |
88 | type 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. |
108 | pub 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)] |
122 | pub struct RestoreState(RawRestoreState); |
123 | |
124 | impl 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)] |
177 | pub 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)] |
194 | pub 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 ] |
213 | pub 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`]. |
239 | pub 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 ] |
278 | macro_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 | |