ostd/sync/
guard.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
// SPDX-License-Identifier: MPL-2.0

use crate::{
    task::{atomic_mode::AsAtomicModeGuard, disable_preempt, DisabledPreemptGuard},
    trap::irq::{disable_local, DisabledLocalIrqGuard},
};

/// A guardian that denotes the guard behavior for holding a spin-based lock.
///
/// It at least ensures that the atomic mode is maintained while the lock is held.
pub trait SpinGuardian {
    /// The guard type for holding a spin lock or a spin-based write lock.
    type Guard: AsAtomicModeGuard + GuardTransfer;
    /// The guard type for holding a spin-based read lock.
    type ReadGuard: AsAtomicModeGuard + GuardTransfer;

    /// Creates a new guard.
    fn guard() -> Self::Guard;
    /// Creates a new read guard.
    fn read_guard() -> Self::ReadGuard;
}

/// The Guard can be transferred atomically.
pub trait GuardTransfer {
    /// Atomically transfers the current guard to a new instance.
    ///
    /// This function ensures that there are no 'gaps' between the destruction of the old guard and
    /// the creation of the new guard, thereby maintaining the atomicity of guard transitions.
    ///
    /// The original guard must be dropped immediately after calling this method.
    fn transfer_to(&mut self) -> Self;
}

/// A guardian that disables preemption while holding a lock.
pub enum PreemptDisabled {}

impl SpinGuardian for PreemptDisabled {
    type Guard = DisabledPreemptGuard;
    type ReadGuard = DisabledPreemptGuard;

    fn guard() -> Self::Guard {
        disable_preempt()
    }
    fn read_guard() -> Self::Guard {
        disable_preempt()
    }
}

/// A guardian that disables IRQs while holding a lock.
///
/// This guardian would incur a certain time overhead over
/// [`PreemptDisabled`]. So prefer avoiding using this guardian when
/// IRQ handlers are allowed to get executed while holding the
/// lock. For example, if a lock is never used in the interrupt
/// context, then it is ok not to use this guardian in the process context.
pub enum LocalIrqDisabled {}

impl SpinGuardian for LocalIrqDisabled {
    type Guard = DisabledLocalIrqGuard;
    type ReadGuard = DisabledLocalIrqGuard;

    fn guard() -> Self::Guard {
        disable_local()
    }
    fn read_guard() -> Self::Guard {
        disable_local()
    }
}

/// A guardian that disables IRQs while holding a write lock.
///
/// This guardian should only be used for a [`RwLock`]. Using it with a [`SpinLock`] will behave in
/// the same way as using [`LocalIrqDisabled`].
///
/// When using this guardian with a [`RwLock`], holding the read lock will only disable preemption,
/// but holding a write lock will disable local IRQs. The user must ensure that the IRQ handlers
/// never take the write lock, so we can take the read lock without disabling IRQs, but we are
/// still free of deadlock even if the IRQ handlers are triggered in the middle.
///
/// [`RwLock`]: super::RwLock
/// [`SpinLock`]: super::SpinLock
pub enum WriteIrqDisabled {}

impl SpinGuardian for WriteIrqDisabled {
    type Guard = DisabledLocalIrqGuard;
    type ReadGuard = DisabledPreemptGuard;

    fn guard() -> Self::Guard {
        disable_local()
    }
    fn read_guard() -> Self::ReadGuard {
        disable_preempt()
    }
}