1use core::marker::PhantomData;
4
5#[doc(hidden)]
10#[inline(always)]
11pub fn irq_save_and_disable() -> usize {
12 imp::irq_save_and_disable()
13}
14
15#[doc(hidden)]
22#[inline(always)]
23pub unsafe fn irq_restore(state: usize) {
24 imp::irq_restore(state);
25}
26
27#[doc(hidden)]
29pub trait GuardState {
30 type State: Clone + Copy;
32
33 fn acquire() -> Self::State;
35
36 fn release(state: Self::State);
38
39 fn lockdep_enabled() -> bool {
41 false
42 }
43}
44
45pub(crate) struct PendingGuardState<G: GuardState> {
51 state: Option<G::State>,
52}
53
54impl<G: GuardState> PendingGuardState<G> {
55 #[inline(always)]
56 pub(crate) fn acquire() -> Self {
57 Self {
58 state: Some(G::acquire()),
59 }
60 }
61
62 #[inline(always)]
63 pub(crate) fn into_state(mut self) -> G::State {
64 self.state
65 .take()
66 .expect("pending guard state must be present")
67 }
68}
69
70impl<G: GuardState> Drop for PendingGuardState<G> {
71 #[inline(always)]
72 fn drop(&mut self) {
73 if let Some(state) = self.state.take() {
74 G::release(state);
75 }
76 }
77}
78
79#[doc(hidden)]
81pub struct RawState;
82
83#[doc(hidden)]
85pub struct PreemptState;
86
87impl PreemptState {
88 pub(crate) fn release_from_irq_return(state: usize) {
89 imp::enable_preempt_from_irq_return(state);
90 }
91}
92
93#[doc(hidden)]
95pub struct IrqSaveState;
96
97#[doc(hidden)]
99pub struct PreemptIrqSaveState;
100
101impl GuardState for RawState {
102 type State = ();
103
104 #[inline(always)]
105 fn acquire() -> Self::State {}
106
107 #[inline(always)]
108 fn release(_state: Self::State) {}
109}
110
111impl GuardState for PreemptState {
112 type State = usize;
113
114 #[inline(always)]
115 fn acquire() -> Self::State {
116 imp::disable_preempt()
117 }
118
119 #[inline(always)]
120 fn release(state: Self::State) {
121 imp::enable_preempt(state);
122 }
123
124 fn lockdep_enabled() -> bool {
125 true
126 }
127}
128
129impl GuardState for IrqSaveState {
130 type State = usize;
131
132 #[inline(always)]
133 fn acquire() -> Self::State {
134 imp::irq_save_and_disable()
135 }
136
137 #[inline(always)]
138 fn release(state: Self::State) {
139 imp::irq_restore(state);
140 }
141}
142
143impl GuardState for PreemptIrqSaveState {
144 type State = usize;
145
146 #[inline(always)]
147 fn acquire() -> Self::State {
148 let preemption = imp::disable_preempt();
149 assert_eq!(preemption & 1, 0, "preemption token must be aligned");
150 preemption | (imp::irq_save_and_disable() & 1)
151 }
152
153 #[inline(always)]
154 fn release(state: Self::State) {
155 imp::irq_restore(state & 1);
156 imp::enable_preempt(state & !1);
157 }
158
159 fn lockdep_enabled() -> bool {
160 true
161 }
162}
163
164pub struct PreemptGuard {
174 state: <PreemptState as GuardState>::State,
175 _not_send: PhantomData<*mut ()>,
176}
177
178impl PreemptGuard {
179 pub fn new() -> Self {
181 Self {
182 state: PreemptState::acquire(),
183 _not_send: PhantomData,
184 }
185 }
186}
187
188impl Default for PreemptGuard {
189 fn default() -> Self {
190 Self::new()
191 }
192}
193
194impl Drop for PreemptGuard {
195 fn drop(&mut self) {
196 PreemptState::release(self.state);
197 }
198}
199
200pub struct IrqSaveGuard {
210 state: <IrqSaveState as GuardState>::State,
211 _not_send: PhantomData<*mut ()>,
212}
213
214impl IrqSaveGuard {
215 pub fn new() -> Self {
217 Self {
218 state: IrqSaveState::acquire(),
219 _not_send: PhantomData,
220 }
221 }
222}
223
224impl Default for IrqSaveGuard {
225 fn default() -> Self {
226 Self::new()
227 }
228}
229
230impl Drop for IrqSaveGuard {
231 fn drop(&mut self) {
232 IrqSaveState::release(self.state);
233 }
234}
235
236pub struct PreemptIrqSaveGuard {
249 state: <PreemptIrqSaveState as GuardState>::State,
250 _not_send: PhantomData<*mut ()>,
251}
252
253impl PreemptIrqSaveGuard {
254 pub fn new() -> Self {
256 Self {
257 state: PreemptIrqSaveState::acquire(),
258 _not_send: PhantomData,
259 }
260 }
261}
262
263impl Default for PreemptIrqSaveGuard {
264 fn default() -> Self {
265 Self::new()
266 }
267}
268
269impl Drop for PreemptIrqSaveGuard {
270 fn drop(&mut self) {
271 PreemptIrqSaveState::release(self.state);
272 }
273}
274
275#[cfg(all(feature = "host-test", not(target_os = "none")))]
276mod imp {
277 use std::cell::{Cell, RefCell};
278
279 std::thread_local! {
280 static PREEMPT_DEPTH: Cell<usize> = const { Cell::new(0) };
281 static IRQ_ENABLED: Cell<bool> = const { Cell::new(true) };
282 static EVENTS: RefCell<std::vec::Vec<&'static str>> = const { RefCell::new(std::vec::Vec::new()) };
283 }
284
285 pub(super) fn disable_preempt() -> usize {
286 EVENTS.with_borrow_mut(|events| events.push("preempt-disable"));
287 PREEMPT_DEPTH.set(PREEMPT_DEPTH.get() + 1);
288 0
289 }
290
291 pub(super) fn enable_preempt(_state: usize) {
292 EVENTS.with_borrow_mut(|events| events.push("preempt-enable"));
293 PREEMPT_DEPTH.set(
294 PREEMPT_DEPTH
295 .get()
296 .checked_sub(1)
297 .expect("unbalanced preemption guard"),
298 );
299 }
300
301 pub(super) fn enable_preempt_from_irq_return(state: usize) {
302 enable_preempt(state);
303 }
304
305 pub(super) fn irq_save_and_disable() -> usize {
306 EVENTS.with_borrow_mut(|events| events.push("irq-disable"));
307 let was_enabled = IRQ_ENABLED.replace(false);
308 usize::from(was_enabled)
309 }
310
311 pub(super) fn irq_restore(state: usize) {
312 EVENTS.with_borrow_mut(|events| events.push("irq-restore"));
313 IRQ_ENABLED.set(state != 0);
314 }
315
316 pub(super) fn snapshot() -> (usize, bool) {
317 (PREEMPT_DEPTH.get(), IRQ_ENABLED.get())
318 }
319
320 #[cfg(all(test, feature = "host-test", not(target_os = "none")))]
321 pub(super) fn take_events() -> std::vec::Vec<&'static str> {
322 EVENTS.take()
323 }
324
325 pub(super) fn preempt_depth() -> usize {
326 PREEMPT_DEPTH.get()
327 }
328
329 #[cfg(feature = "preempt")]
330 pub(super) fn finish_initial_context_switch() {
331 assert_eq!(
332 PREEMPT_DEPTH.get(),
333 1,
334 "initial host context switch must inherit one exclusion depth"
335 );
336 PREEMPT_DEPTH.set(0);
337 }
338}
339
340#[cfg(not(all(feature = "host-test", not(target_os = "none"))))]
341mod imp {
342 #[inline(always)]
343 pub(super) fn disable_preempt() -> usize {
344 #[cfg(feature = "multitask")]
345 return crate::disable_preempt();
346 #[cfg(not(feature = "multitask"))]
347 0
348 }
349
350 #[inline(always)]
351 pub(super) fn enable_preempt(state: usize) {
352 #[cfg(feature = "multitask")]
353 crate::enable_preempt(state);
354 #[cfg(not(feature = "multitask"))]
355 let _ = state;
356 }
357
358 #[inline(always)]
359 pub(super) fn enable_preempt_from_irq_return(state: usize) {
360 #[cfg(feature = "multitask")]
361 crate::enable_preempt_from_irq_return(state);
362 #[cfg(not(feature = "multitask"))]
363 let _ = state;
364 }
365
366 #[inline(always)]
367 pub(super) fn irq_save_and_disable() -> usize {
368 let was_enabled = ax_hal::asm::irqs_enabled();
369 ax_hal::asm::disable_irqs();
370 usize::from(was_enabled)
371 }
372
373 #[inline(always)]
374 pub(super) fn irq_restore(state: usize) {
375 if state != 0 {
376 ax_hal::asm::enable_irqs();
377 } else {
378 ax_hal::asm::disable_irqs();
379 }
380 }
381}
382
383#[cfg(all(feature = "host-test", not(target_os = "none")))]
385#[doc(hidden)]
386pub fn host_preempt_depth() -> usize {
387 imp::preempt_depth()
388}
389
390#[cfg(all(feature = "preempt", feature = "host-test", not(target_os = "none")))]
391pub(crate) fn finish_initial_host_context_switch() {
392 imp::finish_initial_context_switch();
393}
394
395#[cfg(all(feature = "host-test", not(target_os = "none")))]
396pub(crate) fn host_context_snapshot() -> (usize, bool) {
397 imp::snapshot()
398}
399
400#[cfg(all(test, feature = "host-test", not(target_os = "none")))]
401mod tests {
402 use super::{IrqSaveGuard, PreemptGuard, PreemptIrqSaveGuard, imp};
403
404 #[test]
405 fn preempt_guard_nests_and_restores_depth() {
406 assert_eq!(imp::snapshot(), (0, true));
407 let outer = PreemptGuard::new();
408 assert_eq!(imp::snapshot(), (1, true));
409 {
410 let _inner = PreemptGuard::new();
411 assert_eq!(imp::snapshot(), (2, true));
412 }
413 assert_eq!(imp::snapshot(), (1, true));
414 drop(outer);
415 assert_eq!(imp::snapshot(), (0, true));
416 }
417
418 #[test]
419 fn irq_save_guard_preserves_nested_disabled_state() {
420 assert_eq!(imp::snapshot(), (0, true));
421 let outer = IrqSaveGuard::new();
422 assert_eq!(imp::snapshot(), (0, false));
423 {
424 let _inner = IrqSaveGuard::new();
425 assert_eq!(imp::snapshot(), (0, false));
426 }
427 assert_eq!(imp::snapshot(), (0, false));
428 drop(outer);
429 assert_eq!(imp::snapshot(), (0, true));
430 }
431
432 #[test]
433 fn combined_guard_restores_irq_before_preempt_context() {
434 assert_eq!(imp::snapshot(), (0, true));
435 let _ = imp::take_events();
436 let guard = PreemptIrqSaveGuard::new();
437 assert_eq!(imp::snapshot(), (1, false));
438 drop(guard);
439 assert_eq!(imp::snapshot(), (0, true));
440 assert_eq!(
441 imp::take_events(),
442 [
443 "preempt-disable",
444 "irq-disable",
445 "irq-restore",
446 "preempt-enable"
447 ]
448 );
449 }
450}