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 crate::disable_preempt()
345 }
346
347 #[inline(always)]
348 pub(super) fn enable_preempt(state: usize) {
349 crate::enable_preempt(state);
350 }
351
352 #[inline(always)]
353 pub(super) fn enable_preempt_from_irq_return(state: usize) {
354 crate::enable_preempt_from_irq_return(state);
355 }
356
357 #[inline(always)]
358 pub(super) fn irq_save_and_disable() -> usize {
359 let was_enabled = ax_hal::asm::irqs_enabled();
360 ax_hal::asm::disable_irqs();
361 usize::from(was_enabled)
362 }
363
364 #[inline(always)]
365 pub(super) fn irq_restore(state: usize) {
366 if state != 0 {
367 ax_hal::asm::enable_irqs();
368 } else {
369 ax_hal::asm::disable_irqs();
370 }
371 }
372}
373
374#[cfg(all(feature = "host-test", not(target_os = "none")))]
376#[doc(hidden)]
377pub fn host_preempt_depth() -> usize {
378 imp::preempt_depth()
379}
380
381#[cfg(all(feature = "preempt", feature = "host-test", not(target_os = "none")))]
382pub(crate) fn finish_initial_host_context_switch() {
383 imp::finish_initial_context_switch();
384}
385
386#[cfg(all(feature = "host-test", not(target_os = "none")))]
387pub(crate) fn host_context_snapshot() -> (usize, bool) {
388 imp::snapshot()
389}
390
391#[cfg(all(test, feature = "host-test", not(target_os = "none")))]
392mod tests {
393 use super::{IrqSaveGuard, PreemptGuard, PreemptIrqSaveGuard, imp};
394
395 #[test]
396 fn preempt_guard_nests_and_restores_depth() {
397 assert_eq!(imp::snapshot(), (0, true));
398 let outer = PreemptGuard::new();
399 assert_eq!(imp::snapshot(), (1, true));
400 {
401 let _inner = PreemptGuard::new();
402 assert_eq!(imp::snapshot(), (2, true));
403 }
404 assert_eq!(imp::snapshot(), (1, true));
405 drop(outer);
406 assert_eq!(imp::snapshot(), (0, true));
407 }
408
409 #[test]
410 fn irq_save_guard_preserves_nested_disabled_state() {
411 assert_eq!(imp::snapshot(), (0, true));
412 let outer = IrqSaveGuard::new();
413 assert_eq!(imp::snapshot(), (0, false));
414 {
415 let _inner = IrqSaveGuard::new();
416 assert_eq!(imp::snapshot(), (0, false));
417 }
418 assert_eq!(imp::snapshot(), (0, false));
419 drop(outer);
420 assert_eq!(imp::snapshot(), (0, true));
421 }
422
423 #[test]
424 fn combined_guard_restores_irq_before_preempt_context() {
425 assert_eq!(imp::snapshot(), (0, true));
426 let _ = imp::take_events();
427 let guard = PreemptIrqSaveGuard::new();
428 assert_eq!(imp::snapshot(), (1, false));
429 drop(guard);
430 assert_eq!(imp::snapshot(), (0, true));
431 assert_eq!(
432 imp::take_events(),
433 [
434 "preempt-disable",
435 "irq-disable",
436 "irq-restore",
437 "preempt-enable"
438 ]
439 );
440 }
441}