interrupts/lib.rs
1//! Cross-architecture utilities for temporarily disabling interrupts.
2//!
3//! This crate allows you to temporarily disable interrupts and then restore the previous state again.
4//!
5//! Supported architectures:
6//!
7//! - AArch64 (`arch = aarch64`)
8//!
9//! - 64-bit RISC-V (`arch = riscv64`)
10//!
11//! - x86-64 (`arch = x86_64`)
12//!
13//! On other architectures, this crate does nothing.
14//!
15//! # Caveats
16//!
17//! <div class="warning">Interrupts are disabled on a best-effort basis.</div>
18//!
19//! Even though this crate makes sure that interrupts are disabled, nothing prevents you from manually enabling them again.
20//!
21//! [Manually dropping] [`Guard`]s may also cause interrupts to be enabled.
22//!
23//! [Manually dropping]: Guard#caveats-drop-order
24//!
25//! # Examples
26//!
27//! Use [`disable`] to disable interrupts with a guard:
28//!
29//! ```no_run
30//! // interrupts may or may not be enabled
31//! let guard = interrupts::disable();
32//! // interrupts are disabled
33//! drop(guard);
34//! // interrupts are restored to the previous state
35//! ```
36//!
37//! Use [`without`] to run a closure with disabled interrupts:
38//!
39//! ```no_run
40//! // interrupts may or may not be enabled
41//! interrupts::without(|| {
42//! // interrupts are disabled
43//! });
44//! // interrupts are restored to the previous state
45//! ```
46//!
47//! # Related Crates
48//!
49//! - [interrupt-ref-cell] (A `RefCell` for sharing data with interrupt handlers on the same thread.)
50//! - [interrupt-mutex] (A mutex for sharing data with interrupt handlers.)
51//!
52//! [interrupt-ref-cell]: https://crates.io/crates/interrupt-ref-cell
53//! [interrupt-mutex]: https://crates.io/crates/interrupt-mutex
54
55#![no_std]
56
57mod imp;
58
59use core::marker::PhantomData;
60
61/// Temporarily disable interrupts.
62///
63/// Interrupts are enabled once the returned [`Guard`] is dropped.
64///
65/// # Examples
66///
67/// ```no_run
68/// // interrupts may or may not be enabled
69/// let guard = interrupts::disable();
70/// // interrupts are disabled
71/// drop(guard);
72/// // interrupts are restored to the previous state
73/// ```
74#[inline]
75pub fn disable() -> Guard {
76 Guard {
77 flags: imp::read_disable(),
78 _not_send: PhantomData,
79 }
80}
81
82/// An interrupt guard.
83///
84/// Created using [`disable`].
85///
86/// While an instance of this guard is held, interrupts are disabled.
87/// When this guard is dropped, interrupts are restored to the state before disabling.
88///
89/// # Caveats (Drop Order)
90///
91/// If interrupts are enabled, acquiring a guard will disable them.
92/// Dropping this guard will enable interrupts again.
93/// Different [`Guard`]s might be dropped in arbitrary order.
94///
95/// This may result in interrupts being enabled again, even though another [`Guard`] is still held.
96/// For this to happen, one must explicitly drop guards in the wrong order, though.
97/// As long as guards don't leave their original [drop scope], they are dropped automatically in the correct order.
98///
99/// [drop scope]: https://doc.rust-lang.org/reference/destructors.html#drop-scopes
100///
101/// # Examples
102///
103/// ```no_run
104/// // interrupts may or may not be enabled
105/// let guard = interrupts::disable();
106/// // interrupts are disabled
107/// drop(guard);
108/// // interrupts are restored to the previous state
109/// ```
110///
111/// Dropping guards in the wrong order (don't do this):
112///
113/// ```no_run
114/// // Interrupts are enabled
115/// let a = interrupts::disable();
116/// // Interrupts are disabled
117/// let b = interrupts::disable();
118/// drop(a);
119/// // Interrupts are enabled, although we still hold a guard in b
120/// drop(b);
121/// ```
122pub struct Guard {
123 flags: imp::Flags,
124 /// Interrupts are per hardware thread.
125 ///
126 /// Making Guard `!Send` avoids disabling interrupts on one hardware thread and restoring on another.
127 _not_send: PhantomData<*mut ()>,
128}
129
130impl Guard {
131 /// ```compile_fail
132 /// fn send<T: Send>(_: T) {}
133 ///
134 /// send(interrupts::disable());
135 /// ```
136 fn _dummy() {}
137}
138
139impl Drop for Guard {
140 #[inline]
141 fn drop(&mut self) {
142 #[allow(clippy::unit_arg)]
143 imp::restore(self.flags);
144 }
145}
146
147/// Run a closure with disabled interrupts.
148///
149/// Run the given closure, disabling interrupts before running it (if they aren't already disabled).
150/// Afterward, interrupts are enabled again if they were enabled before.
151///
152/// If you have other `enable` and `disable` calls _within_ the closure, things may not work as expected.
153///
154/// Only has an effect if `target_os = "none"`.
155///
156/// # Examples
157///
158/// ```no_run
159/// // interrupts may or may not be enabled
160/// interrupts::without(|| {
161/// // interrupts are disabled
162/// });
163/// // interrupts are restored to the previous state
164/// ```
165///
166/// Nesting:
167///
168/// ```no_run
169/// // interrupts may be enabled
170/// interrupts::without(|| {
171/// // interrupts are disabled
172/// interrupts::without(|| {
173/// // interrupts are disabled
174/// });
175/// // interrupts are still disabled
176/// });
177/// // interrupts are restored
178/// ```
179// Docs adapted from `x86_64::instructions::interrupts::without_interrupts`.
180#[inline]
181pub fn without<F, R>(f: F) -> R
182where
183 F: FnOnce() -> R,
184{
185 let guard = disable();
186
187 let ret = f();
188
189 drop(guard);
190
191 ret
192}