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
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
//! A mutex for sharing data with interrupt handlers.
//!
//! Using normal mutexes to share data with interrupt handlers may result in deadlocks.
//! This is because interrupts may be raised while the mutex is being held on the same thread.
//!
//! [`InterruptMutex`] wraps another mutex and disables interrupts while the inner mutex is locked.
//! When the mutex is unlocked, the previous interrupt state is restored.
//! This makes [`InterruptMutex`] suitable for sharing data with interrupts.
//!
//! When used in bare-metal environments with spinlocks, locking the mutex corresponds to Linux's `spin_lock_irqsave` and unlocking corresponds to `spin_unlock_irqrestore`.
//! See the [Unreliable Guide To Locking — The Linux Kernel documentation].
//! While `spin_lock_irqsave(lock, flags)` saves the interrupt flags in the explicit `flags` argument, [`InterruptMutex`] saves the interrupt flags internally.
//!
//! [Unreliable Guide To Locking — The Linux Kernel documentation]: https://www.kernel.org/doc/html/latest/kernel-hacking/locking.html#locking-between-hard-irq-and-softirqs-tasklets
//!
//! [Drop Order]: #caveats
//!
//! # Caveats
//!
//! <div class="warning">Interrupts are disabled on a best-effort basis.</div>
//!
//! Holding an [`InterruptMutexGuard`] does not guarantee that interrupts are disabled.
//! Dropping guards from different [`InterruptMutex`]es in the wrong order might enable interrupts prematurely.
//! Similarly, you can just enable interrupts manually while holding a guard.
//!
//! # Examples
//!
//! ```no_run
//! // Make a mutex of your choice into an `InterruptMutex`.
//! type InterruptMutex<T> = interrupt_mutex::InterruptMutex<parking_lot::RawMutex, T>;
//!
//! static X: InterruptMutex<Vec<i32>> = InterruptMutex::new(Vec::new());
//!
//! fn interrupt_handler() {
//! X.lock().push(1);
//! }
//! #
//! # fn raise_interrupt() {}
//!
//! let v = X.lock();
//! // Raise an interrupt
//! raise_interrupt();
//! assert_eq!(*v, vec![]);
//! drop(v);
//!
//! // The interrupt handler runs
//!
//! let v = X.lock();
//! assert_eq!(*v, vec![1]);
//! drop(v);
//! ```
use UnsafeCell;
use MaybeUninit;
use ;
/// A mutex for sharing data with interrupt handlers.
///
/// This mutex wraps another [`RawMutex`] and disables interrupts while locked.
// SAFETY: The `UnsafeCell` is locked by `inner`, initialized on `lock` and uninitialized on `unlock`.
unsafe
// SAFETY: Mutexes cannot be send to other threads while locked.
// Sending them while unlocked is fine.
unsafe
unsafe
/// A [`lock_api::Mutex`] based on [`RawInterruptMutex`].
pub type InterruptMutex<I, T> = Mutex;
/// A [`lock_api::MutexGuard`] based on [`RawInterruptMutex`].
pub type InterruptMutexGuard<'a, I, T> = MutexGuard;