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
129
130
131
132
133
134
135
136
137
138
139
140
141
//! A mutex for sharing data with interrupt handlers or signal 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
//!
//! ```
//! // 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);
//! }
//! #
//! # // Setup signal handling for demo
//! #
//! # use nix::libc;
//! # use nix::sys::signal::{self, SigHandler, Signal};
//! #
//! # extern "C" fn handle_sigint(_signal: libc::c_int) {
//! # interrupt_handler();
//! # }
//! #
//! # let handler = SigHandler::Handler(handle_sigint);
//! # unsafe { signal::signal(Signal::SIGINT, handler) }.unwrap();
//! #
//! # fn raise_interrupt() {
//! # signal::raise(Signal::SIGINT);
//! # }
//!
//! 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 or signal 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;