nu_protocol/pipeline/signals.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
use crate::{ShellError, Span};
use std::sync::{
atomic::{AtomicBool, Ordering},
Arc,
};
use serde::{Deserialize, Serialize};
/// Used to check for signals to suspend or terminate the execution of Nushell code.
///
/// For now, this struct only supports interruption (ctrl+c or SIGINT).
#[derive(Debug, Clone)]
pub struct Signals {
signals: Option<Arc<AtomicBool>>,
}
impl Signals {
/// A [`Signals`] that is not hooked up to any event/signals source.
///
/// So, this [`Signals`] will never be interrupted.
pub const EMPTY: Self = Signals { signals: None };
/// Create a new [`Signals`] with `ctrlc` as the interrupt source.
///
/// Once `ctrlc` is set to `true`, [`check`](Self::check) will error
/// and [`interrupted`](Self::interrupted) will return `true`.
pub fn new(ctrlc: Arc<AtomicBool>) -> Self {
Self {
signals: Some(ctrlc),
}
}
/// Create a [`Signals`] that is not hooked up to any event/signals source.
///
/// So, the returned [`Signals`] will never be interrupted.
///
/// This should only be used in test code, or if the stream/iterator being created
/// already has an underlying [`Signals`].
pub const fn empty() -> Self {
Self::EMPTY
}
/// Returns an `Err` if an interrupt has been triggered.
///
/// Otherwise, returns `Ok`.
#[inline]
pub fn check(&self, span: Span) -> Result<(), ShellError> {
#[inline]
#[cold]
fn interrupt_error(span: Span) -> Result<(), ShellError> {
Err(ShellError::Interrupted { span })
}
if self.interrupted() {
interrupt_error(span)
} else {
Ok(())
}
}
/// Triggers an interrupt.
pub fn trigger(&self) {
if let Some(signals) = &self.signals {
signals.store(true, Ordering::Relaxed);
}
}
/// Returns whether an interrupt has been triggered.
#[inline]
pub fn interrupted(&self) -> bool {
self.signals
.as_deref()
.is_some_and(|b| b.load(Ordering::Relaxed))
}
pub(crate) fn is_empty(&self) -> bool {
self.signals.is_none()
}
pub fn reset(&self) {
if let Some(signals) = &self.signals {
signals.store(false, Ordering::Relaxed);
}
}
}
/// The types of things that can be signaled. It's anticipated this will change as we learn more
/// about how we'd like signals to be handled.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum SignalAction {
Interrupt,
Reset,
}