mirror of
https://github.com/nushell/nushell.git
synced 2025-08-10 04:48:20 +02:00
Add and use new Signals
struct (#13314)
# Description This PR introduces a new `Signals` struct to replace our adhoc passing around of `ctrlc: Option<Arc<AtomicBool>>`. Doing so has a few benefits: - We can better enforce when/where resetting or triggering an interrupt is allowed. - Consolidates `nu_utils::ctrl_c::was_pressed` and other ad-hoc re-implementations into a single place: `Signals::check`. - This allows us to add other types of signals later if we want. E.g., exiting or suspension. - Similarly, we can more easily change the underlying implementation if we need to in the future. - Places that used to have a `ctrlc` of `None` now use `Signals::empty()`, so we can double check these usages for correctness in the future.
This commit is contained in:
@ -3,23 +3,21 @@ use nu_engine::{get_eval_block_with_early_return, get_full_help, ClosureEvalOnce
|
||||
use nu_protocol::{
|
||||
ast::Call,
|
||||
engine::{Closure, EngineState, Redirection, Stack},
|
||||
Config, IntoSpanned, OutDest, PipelineData, PluginIdentity, ShellError, Span, Spanned, Value,
|
||||
Config, IntoSpanned, OutDest, PipelineData, PluginIdentity, ShellError, Signals, Span, Spanned,
|
||||
Value,
|
||||
};
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
collections::HashMap,
|
||||
sync::{
|
||||
atomic::{AtomicBool, AtomicU32},
|
||||
Arc,
|
||||
},
|
||||
sync::{atomic::AtomicU32, Arc},
|
||||
};
|
||||
|
||||
/// Object safe trait for abstracting operations required of the plugin context.
|
||||
pub trait PluginExecutionContext: Send + Sync {
|
||||
/// A span pointing to the command being executed
|
||||
fn span(&self) -> Span;
|
||||
/// The interrupt signal, if present
|
||||
fn ctrlc(&self) -> Option<&Arc<AtomicBool>>;
|
||||
/// The [`Signals`] struct, if present
|
||||
fn signals(&self) -> &Signals;
|
||||
/// The pipeline externals state, for tracking the foreground process group, if present
|
||||
fn pipeline_externals_state(&self) -> Option<&Arc<(AtomicU32, AtomicU32)>>;
|
||||
/// Get engine configuration
|
||||
@ -80,8 +78,8 @@ impl<'a> PluginExecutionContext for PluginExecutionCommandContext<'a> {
|
||||
self.call.head
|
||||
}
|
||||
|
||||
fn ctrlc(&self) -> Option<&Arc<AtomicBool>> {
|
||||
self.engine_state.ctrlc.as_ref()
|
||||
fn signals(&self) -> &Signals {
|
||||
self.engine_state.signals()
|
||||
}
|
||||
|
||||
fn pipeline_externals_state(&self) -> Option<&Arc<(AtomicU32, AtomicU32)>> {
|
||||
@ -234,8 +232,8 @@ impl PluginExecutionContext for PluginExecutionBogusContext {
|
||||
Span::test_data()
|
||||
}
|
||||
|
||||
fn ctrlc(&self) -> Option<&Arc<AtomicBool>> {
|
||||
None
|
||||
fn signals(&self) -> &Signals {
|
||||
&Signals::EMPTY
|
||||
}
|
||||
|
||||
fn pipeline_externals_state(&self) -> Option<&Arc<(AtomicU32, AtomicU32)>> {
|
||||
|
@ -12,11 +12,11 @@ use nu_plugin_protocol::{
|
||||
};
|
||||
use nu_protocol::{
|
||||
ast::Operator, CustomValue, IntoSpanned, PipelineData, PluginMetadata, PluginSignature,
|
||||
ShellError, Span, Spanned, Value,
|
||||
ShellError, Signals, Span, Spanned, Value,
|
||||
};
|
||||
use std::{
|
||||
collections::{btree_map, BTreeMap},
|
||||
sync::{atomic::AtomicBool, mpsc, Arc, OnceLock},
|
||||
sync::{mpsc, Arc, OnceLock},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
@ -103,8 +103,8 @@ struct PluginCallState {
|
||||
/// Don't try to send the plugin call response. This is only used for `Dropped` to avoid an
|
||||
/// error
|
||||
dont_send_response: bool,
|
||||
/// Interrupt signal to be used for stream iterators
|
||||
ctrlc: Option<Arc<AtomicBool>>,
|
||||
/// Signals to be used for stream iterators
|
||||
signals: Signals,
|
||||
/// Channel to receive context on to be used if needed
|
||||
context_rx: Option<mpsc::Receiver<Context>>,
|
||||
/// Span associated with the call, if any
|
||||
@ -231,14 +231,14 @@ impl PluginInterfaceManager {
|
||||
}
|
||||
}
|
||||
|
||||
/// Find the ctrlc signal corresponding to the given plugin call id
|
||||
fn get_ctrlc(&mut self, id: PluginCallId) -> Result<Option<Arc<AtomicBool>>, ShellError> {
|
||||
/// Find the [`Signals`] struct corresponding to the given plugin call id
|
||||
fn get_signals(&mut self, id: PluginCallId) -> Result<Signals, ShellError> {
|
||||
// Make sure we're up to date
|
||||
self.receive_plugin_call_subscriptions();
|
||||
// Find the subscription and return the context
|
||||
self.plugin_call_states
|
||||
.get(&id)
|
||||
.map(|state| state.ctrlc.clone())
|
||||
.map(|state| state.signals.clone())
|
||||
.ok_or_else(|| ShellError::PluginFailedToDecode {
|
||||
msg: format!("Unknown plugin call ID: {id}"),
|
||||
})
|
||||
@ -517,14 +517,14 @@ impl InterfaceManager for PluginInterfaceManager {
|
||||
// Handle reading the pipeline data, if any
|
||||
let response = response
|
||||
.map_data(|data| {
|
||||
let ctrlc = self.get_ctrlc(id)?;
|
||||
let signals = self.get_signals(id)?;
|
||||
|
||||
// Register the stream in the response
|
||||
if let Some(stream_id) = data.stream_id() {
|
||||
self.recv_stream_started(id, stream_id);
|
||||
}
|
||||
|
||||
self.read_pipeline_data(data, ctrlc.as_ref())
|
||||
self.read_pipeline_data(data, &signals)
|
||||
})
|
||||
.unwrap_or_else(|err| {
|
||||
// If there's an error with initializing this stream, change it to a plugin
|
||||
@ -544,8 +544,8 @@ impl InterfaceManager for PluginInterfaceManager {
|
||||
let call = call
|
||||
// Handle reading the pipeline data, if any
|
||||
.map_data(|input| {
|
||||
let ctrlc = self.get_ctrlc(context)?;
|
||||
self.read_pipeline_data(input, ctrlc.as_ref())
|
||||
let signals = self.get_signals(context)?;
|
||||
self.read_pipeline_data(input, &signals)
|
||||
})
|
||||
// Do anything extra needed for each engine call setup
|
||||
.and_then(|mut engine_call| {
|
||||
@ -698,7 +698,9 @@ impl PluginInterface {
|
||||
context: Option<&dyn PluginExecutionContext>,
|
||||
) -> Result<WritePluginCallResult, ShellError> {
|
||||
let id = self.state.plugin_call_id_sequence.next()?;
|
||||
let ctrlc = context.and_then(|c| c.ctrlc().cloned());
|
||||
let signals = context
|
||||
.map(|c| c.signals().clone())
|
||||
.unwrap_or_else(Signals::empty);
|
||||
let (tx, rx) = mpsc::channel();
|
||||
let (context_tx, context_rx) = mpsc::channel();
|
||||
let keep_plugin_custom_values = mpsc::channel();
|
||||
@ -746,7 +748,7 @@ impl PluginInterface {
|
||||
PluginCallState {
|
||||
sender: Some(tx).filter(|_| !dont_send_response),
|
||||
dont_send_response,
|
||||
ctrlc,
|
||||
signals,
|
||||
context_rx: Some(context_rx),
|
||||
span: call.span(),
|
||||
keep_plugin_custom_values,
|
||||
|
@ -18,7 +18,7 @@ use nu_protocol::{
|
||||
ast::{Math, Operator},
|
||||
engine::Closure,
|
||||
ByteStreamType, CustomValue, IntoInterruptiblePipelineData, IntoSpanned, PipelineData,
|
||||
PluginMetadata, PluginSignature, ShellError, Span, Spanned, Value,
|
||||
PluginMetadata, PluginSignature, ShellError, Signals, Span, Spanned, Value,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{
|
||||
@ -56,7 +56,7 @@ fn manager_consume_all_exits_after_streams_and_interfaces_are_dropped() -> Resul
|
||||
id: 0,
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
None,
|
||||
&Signals::empty(),
|
||||
)?;
|
||||
|
||||
// and an interface...
|
||||
@ -112,7 +112,7 @@ fn manager_consume_all_propagates_io_error_to_readers() -> Result<(), ShellError
|
||||
id: 0,
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
None,
|
||||
&Signals::empty(),
|
||||
)?;
|
||||
|
||||
manager
|
||||
@ -159,7 +159,7 @@ fn manager_consume_all_propagates_message_error_to_readers() -> Result<(), Shell
|
||||
span: Span::test_data(),
|
||||
type_: ByteStreamType::Unknown,
|
||||
}),
|
||||
None,
|
||||
&Signals::empty(),
|
||||
)?;
|
||||
|
||||
manager
|
||||
@ -190,7 +190,7 @@ fn fake_plugin_call(
|
||||
PluginCallState {
|
||||
sender: Some(tx),
|
||||
dont_send_response: false,
|
||||
ctrlc: None,
|
||||
signals: Signals::empty(),
|
||||
context_rx: None,
|
||||
span: None,
|
||||
keep_plugin_custom_values: mpsc::channel(),
|
||||
@ -493,7 +493,7 @@ fn manager_handle_engine_call_after_response_received() -> Result<(), ShellError
|
||||
PluginCallState {
|
||||
sender: None,
|
||||
dont_send_response: false,
|
||||
ctrlc: None,
|
||||
signals: Signals::empty(),
|
||||
context_rx: Some(context_rx),
|
||||
span: None,
|
||||
keep_plugin_custom_values: mpsc::channel(),
|
||||
@ -559,7 +559,7 @@ fn manager_send_plugin_call_response_removes_context_only_if_no_streams_to_read(
|
||||
PluginCallState {
|
||||
sender: None,
|
||||
dont_send_response: false,
|
||||
ctrlc: None,
|
||||
signals: Signals::empty(),
|
||||
context_rx: None,
|
||||
span: None,
|
||||
keep_plugin_custom_values: mpsc::channel(),
|
||||
@ -595,7 +595,7 @@ fn manager_consume_stream_end_removes_context_only_if_last_stream() -> Result<()
|
||||
PluginCallState {
|
||||
sender: None,
|
||||
dont_send_response: false,
|
||||
ctrlc: None,
|
||||
signals: Signals::empty(),
|
||||
context_rx: None,
|
||||
span: None,
|
||||
keep_plugin_custom_values: mpsc::channel(),
|
||||
@ -678,7 +678,7 @@ fn manager_prepare_pipeline_data_adds_source_to_list_streams() -> Result<(), She
|
||||
[Value::test_custom_value(Box::new(
|
||||
test_plugin_custom_value(),
|
||||
))]
|
||||
.into_pipeline_data(Span::test_data(), None),
|
||||
.into_pipeline_data(Span::test_data(), Signals::empty()),
|
||||
)?;
|
||||
|
||||
let value = data
|
||||
@ -852,7 +852,9 @@ fn interface_write_plugin_call_writes_run_with_stream_input() -> Result<(), Shel
|
||||
positional: vec![],
|
||||
named: vec![],
|
||||
},
|
||||
input: values.clone().into_pipeline_data(Span::test_data(), None),
|
||||
input: values
|
||||
.clone()
|
||||
.into_pipeline_data(Span::test_data(), Signals::empty()),
|
||||
}),
|
||||
None,
|
||||
)?;
|
||||
@ -1148,7 +1150,9 @@ fn interface_prepare_pipeline_data_accepts_normal_streams() -> Result<(), ShellE
|
||||
let values = normal_values(&interface);
|
||||
let state = CurrentCallState::default();
|
||||
let data = interface.prepare_pipeline_data(
|
||||
values.clone().into_pipeline_data(Span::test_data(), None),
|
||||
values
|
||||
.clone()
|
||||
.into_pipeline_data(Span::test_data(), Signals::empty()),
|
||||
&state,
|
||||
)?;
|
||||
|
||||
@ -1211,7 +1215,9 @@ fn interface_prepare_pipeline_data_rejects_bad_custom_value_in_a_stream() -> Res
|
||||
let values = bad_custom_values();
|
||||
let state = CurrentCallState::default();
|
||||
let data = interface.prepare_pipeline_data(
|
||||
values.clone().into_pipeline_data(Span::test_data(), None),
|
||||
values
|
||||
.clone()
|
||||
.into_pipeline_data(Span::test_data(), Signals::empty()),
|
||||
&state,
|
||||
)?;
|
||||
|
||||
|
Reference in New Issue
Block a user