ostd/sync/
guard.rs

1// SPDX-License-Identifier: MPL-2.0
2
3use crate::{
4    irq::{DisabledLocalIrqGuard, disable_local},
5    task::{DisabledPreemptGuard, atomic_mode::AsAtomicModeGuard, disable_preempt},
6};
7
8/// A guardian that denotes the guard behavior for holding a spin-based lock.
9///
10/// It at least ensures that the atomic mode is maintained while the lock is held.
11pub trait SpinGuardian {
12    /// The guard type for holding a spin lock or a spin-based write lock.
13    type Guard: AsAtomicModeGuard + GuardTransfer;
14    /// The guard type for holding a spin-based read lock.
15    type ReadGuard: AsAtomicModeGuard + GuardTransfer;
16
17    /// Creates a new guard.
18    fn guard() -> Self::Guard;
19    /// Creates a new read guard.
20    fn read_guard() -> Self::ReadGuard;
21}
22
23/// The Guard can be transferred atomically.
24pub trait GuardTransfer {
25    /// Atomically transfers the current guard to a new instance.
26    ///
27    /// This function ensures that there are no 'gaps' between the destruction of the old guard and
28    /// the creation of the new guard, thereby maintaining the atomicity of guard transitions.
29    ///
30    /// The original guard must be dropped immediately after calling this method.
31    fn transfer_to(&mut self) -> Self;
32}
33
34/// A guardian that disables preemption while holding a lock.
35pub enum PreemptDisabled {}
36
37impl SpinGuardian for PreemptDisabled {
38    type Guard = DisabledPreemptGuard;
39    type ReadGuard = DisabledPreemptGuard;
40
41    fn guard() -> Self::Guard {
42        disable_preempt()
43    }
44    fn read_guard() -> Self::Guard {
45        disable_preempt()
46    }
47}
48
49/// A guardian that disables IRQs while holding a lock.
50///
51/// This guardian would incur a certain time overhead over
52/// [`PreemptDisabled`]. So prefer avoiding using this guardian when
53/// IRQ handlers are allowed to get executed while holding the
54/// lock. For example, if a lock is never used in the interrupt
55/// context, then it is ok not to use this guardian in the process context.
56pub enum LocalIrqDisabled {}
57
58impl SpinGuardian for LocalIrqDisabled {
59    type Guard = DisabledLocalIrqGuard;
60    type ReadGuard = DisabledLocalIrqGuard;
61
62    fn guard() -> Self::Guard {
63        disable_local()
64    }
65    fn read_guard() -> Self::Guard {
66        disable_local()
67    }
68}
69
70/// A guardian that disables IRQs while holding a write lock.
71///
72/// This guardian should only be used for a [`RwLock`]. Using it with a [`SpinLock`] will behave in
73/// the same way as using [`LocalIrqDisabled`].
74///
75/// When using this guardian with a [`RwLock`], holding the read lock will only disable preemption,
76/// but holding a write lock will disable local IRQs. The user must ensure that the IRQ handlers
77/// never take the write lock, so we can take the read lock without disabling IRQs, but we are
78/// still free of deadlock even if the IRQ handlers are triggered in the middle.
79///
80/// [`RwLock`]: super::RwLock
81/// [`SpinLock`]: super::SpinLock
82pub enum WriteIrqDisabled {}
83
84impl SpinGuardian for WriteIrqDisabled {
85    type Guard = DisabledLocalIrqGuard;
86    type ReadGuard = DisabledPreemptGuard;
87
88    fn guard() -> Self::Guard {
89        disable_local()
90    }
91    fn read_guard() -> Self::ReadGuard {
92        disable_preempt()
93    }
94}