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