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}