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:
Ian Manske
2024-07-07 22:29:01 +00:00
committed by GitHub
parent c6b6b1b7a8
commit 399a7c8836
246 changed files with 1332 additions and 1234 deletions

View File

@@ -3,10 +3,9 @@ use crate::{DirBuilder, DirInfo, FileInfo};
#[allow(deprecated)]
use nu_engine::{command_prelude::*, current_dir};
use nu_glob::Pattern;
use nu_protocol::NuGlob;
use nu_protocol::{NuGlob, Signals};
use serde::Deserialize;
use std::path::Path;
use std::sync::{atomic::AtomicBool, Arc};
#[derive(Clone)]
pub struct Du;
@@ -120,8 +119,8 @@ impl Command for Du {
min_size,
};
Ok(
du_for_one_pattern(args, &current_dir, tag, engine_state.ctrlc.clone())?
.into_pipeline_data(tag, engine_state.ctrlc.clone()),
du_for_one_pattern(args, &current_dir, tag, engine_state.signals())?
.into_pipeline_data(tag, engine_state.signals().clone()),
)
}
Some(paths) => {
@@ -139,7 +138,7 @@ impl Command for Du {
args,
&current_dir,
tag,
engine_state.ctrlc.clone(),
engine_state.signals(),
)?)
}
@@ -147,7 +146,7 @@ impl Command for Du {
Ok(result_iters
.into_iter()
.flatten()
.into_pipeline_data(tag, engine_state.ctrlc.clone()))
.into_pipeline_data(tag, engine_state.signals().clone()))
}
}
}
@@ -164,8 +163,8 @@ impl Command for Du {
fn du_for_one_pattern(
args: DuArgs,
current_dir: &Path,
call_span: Span,
ctrl_c: Option<Arc<AtomicBool>>,
span: Span,
signals: &Signals,
) -> Result<impl Iterator<Item = Value> + Send, ShellError> {
let exclude = args.exclude.map_or(Ok(None), move |x| {
Pattern::new(x.item.as_ref())
@@ -178,7 +177,7 @@ fn du_for_one_pattern(
let include_files = args.all;
let mut paths = match args.path {
Some(p) => nu_engine::glob_from(&p, current_dir, call_span, None),
Some(p) => nu_engine::glob_from(&p, current_dir, span, None),
// The * pattern should never fail.
None => nu_engine::glob_from(
&Spanned {
@@ -186,7 +185,7 @@ fn du_for_one_pattern(
span: Span::unknown(),
},
current_dir,
call_span,
span,
None,
),
}
@@ -205,7 +204,7 @@ fn du_for_one_pattern(
let min_size = args.min_size.map(|f| f.item as u64);
let params = DirBuilder {
tag: call_span,
tag: span,
min: min_size,
deref,
exclude,
@@ -217,13 +216,13 @@ fn du_for_one_pattern(
match p {
Ok(a) => {
if a.is_dir() {
output.push(DirInfo::new(a, &params, max_depth, ctrl_c.clone()).into());
} else if let Ok(v) = FileInfo::new(a, deref, call_span) {
output.push(DirInfo::new(a, &params, max_depth, span, signals)?.into());
} else if let Ok(v) = FileInfo::new(a, deref, span) {
output.push(v.into());
}
}
Err(e) => {
output.push(Value::error(e, call_span));
output.push(Value::error(e, span));
}
}
}

View File

@@ -1,5 +1,5 @@
use nu_engine::command_prelude::*;
use std::sync::{atomic::AtomicBool, Arc};
use nu_protocol::Signals;
use wax::{Glob as WaxGlob, WalkBehavior, WalkEntry};
#[derive(Clone)]
@@ -125,7 +125,6 @@ impl Command for Glob {
call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
let ctrlc = engine_state.ctrlc.clone();
let span = call.head;
let glob_pattern: Spanned<String> = call.req(engine_state, stack, 0)?;
let depth = call.get_flag(engine_state, stack, "depth")?;
@@ -216,7 +215,14 @@ impl Command for Glob {
inner: vec![],
})?
.flatten();
glob_to_value(ctrlc, glob_results, no_dirs, no_files, no_symlinks, span)
glob_to_value(
engine_state.signals(),
glob_results,
no_dirs,
no_files,
no_symlinks,
span,
)
} else {
let glob_results = glob
.walk_with_behavior(
@@ -227,12 +233,19 @@ impl Command for Glob {
},
)
.flatten();
glob_to_value(ctrlc, glob_results, no_dirs, no_files, no_symlinks, span)
glob_to_value(
engine_state.signals(),
glob_results,
no_dirs,
no_files,
no_symlinks,
span,
)
}?;
Ok(result
.into_iter()
.into_pipeline_data(span, engine_state.ctrlc.clone()))
.into_pipeline_data(span, engine_state.signals().clone()))
}
}
@@ -252,7 +265,7 @@ fn convert_patterns(columns: &[Value]) -> Result<Vec<String>, ShellError> {
}
fn glob_to_value<'a>(
ctrlc: Option<Arc<AtomicBool>>,
signals: &Signals,
glob_results: impl Iterator<Item = WalkEntry<'a>>,
no_dirs: bool,
no_files: bool,
@@ -261,10 +274,7 @@ fn glob_to_value<'a>(
) -> Result<Vec<Value>, ShellError> {
let mut result: Vec<Value> = Vec::new();
for entry in glob_results {
if nu_utils::ctrl_c::was_pressed(&ctrlc) {
result.clear();
return Err(ShellError::InterruptedByUser { span: None });
}
signals.check(span)?;
let file_type = entry.file_type();
if !(no_dirs && file_type.is_dir()

View File

@@ -6,14 +6,13 @@ use nu_engine::glob_from;
use nu_engine::{command_prelude::*, env::current_dir};
use nu_glob::MatchOptions;
use nu_path::expand_to_real_path;
use nu_protocol::{DataSource, NuGlob, PipelineMetadata};
use nu_protocol::{DataSource, NuGlob, PipelineMetadata, Signals};
use pathdiff::diff_paths;
#[cfg(unix)]
use std::os::unix::fs::PermissionsExt;
use std::{
path::PathBuf,
sync::Arc,
time::{SystemTime, UNIX_EPOCH},
};
@@ -93,7 +92,6 @@ impl Command for Ls {
let du = call.has_flag(engine_state, stack, "du")?;
let directory = call.has_flag(engine_state, stack, "directory")?;
let use_mime_type = call.has_flag(engine_state, stack, "mime-type")?;
let ctrl_c = engine_state.ctrlc.clone();
let call_span = call.head;
#[allow(deprecated)]
let cwd = current_dir(engine_state, stack)?;
@@ -116,10 +114,10 @@ impl Command for Ls {
Some(pattern_arg)
};
match input_pattern_arg {
None => Ok(ls_for_one_pattern(None, args, ctrl_c.clone(), cwd)?
None => Ok(ls_for_one_pattern(None, args, engine_state.signals(), cwd)?
.into_pipeline_data_with_metadata(
call_span,
ctrl_c,
engine_state.signals().clone(),
PipelineMetadata {
data_source: DataSource::Ls,
content_type: None,
@@ -131,7 +129,7 @@ impl Command for Ls {
result_iters.push(ls_for_one_pattern(
Some(pat),
args,
ctrl_c.clone(),
engine_state.signals(),
cwd.clone(),
)?)
}
@@ -143,7 +141,7 @@ impl Command for Ls {
.flatten()
.into_pipeline_data_with_metadata(
call_span,
ctrl_c,
engine_state.signals().clone(),
PipelineMetadata {
data_source: DataSource::Ls,
content_type: None,
@@ -215,7 +213,7 @@ impl Command for Ls {
fn ls_for_one_pattern(
pattern_arg: Option<Spanned<NuGlob>>,
args: Args,
ctrl_c: Option<Arc<AtomicBool>>,
signals: &Signals,
cwd: PathBuf,
) -> Result<Box<dyn Iterator<Item = Value> + Send>, ShellError> {
let Args {
@@ -342,7 +340,7 @@ fn ls_for_one_pattern(
let mut hidden_dirs = vec![];
let one_ctrl_c = ctrl_c.clone();
let signals = signals.clone();
Ok(Box::new(paths_peek.filter_map(move |x| match x {
Ok(path) => {
let metadata = match std::fs::symlink_metadata(&path) {
@@ -412,7 +410,7 @@ fn ls_for_one_pattern(
call_span,
long,
du,
one_ctrl_c.clone(),
&signals,
use_mime_type,
);
match entry {
@@ -474,7 +472,6 @@ fn path_contains_hidden_folder(path: &Path, folders: &[PathBuf]) -> bool {
#[cfg(unix)]
use std::os::unix::fs::FileTypeExt;
use std::path::Path;
use std::sync::atomic::AtomicBool;
pub fn get_file_type(md: &std::fs::Metadata, display_name: &str, use_mime_type: bool) -> String {
let ft = md.file_type();
@@ -523,7 +520,7 @@ pub(crate) fn dir_entry_dict(
span: Span,
long: bool,
du: bool,
ctrl_c: Option<Arc<AtomicBool>>,
signals: &Signals,
use_mime_type: bool,
) -> Result<Value, ShellError> {
#[cfg(windows)]
@@ -618,7 +615,7 @@ pub(crate) fn dir_entry_dict(
if md.is_dir() {
if du {
let params = DirBuilder::new(Span::new(0, 2), None, false, None, false);
let dir_size = DirInfo::new(filename, &params, None, ctrl_c).get_size();
let dir_size = DirInfo::new(filename, &params, None, span, signals)?.get_size();
Value::filesize(dir_size as i64, span)
} else {

View File

@@ -51,7 +51,6 @@ impl Command for Open {
) -> Result<PipelineData, ShellError> {
let raw = call.has_flag(engine_state, stack, "raw")?;
let call_span = call.head;
let ctrlc = engine_state.ctrlc.clone();
#[allow(deprecated)]
let cwd = current_dir(engine_state, stack)?;
let mut paths = get_rest_for_glob_pattern(engine_state, stack, call, 0)?;
@@ -122,8 +121,12 @@ impl Command for Open {
} else {
#[cfg(feature = "sqlite")]
if !raw {
let res = SQLiteDatabase::try_from_path(path, arg_span, ctrlc.clone())
.map(|db| db.into_value(call.head).into_pipeline_data());
let res = SQLiteDatabase::try_from_path(
path,
arg_span,
engine_state.signals().clone(),
)
.map(|db| db.into_value(call.head).into_pipeline_data());
if res.is_ok() {
return res;
@@ -144,7 +147,7 @@ impl Command for Open {
};
let stream = PipelineData::ByteStream(
ByteStream::file(file, call_span, ctrlc.clone()),
ByteStream::file(file, call_span, engine_state.signals().clone()),
Some(PipelineMetadata {
data_source: DataSource::FilePath(path.to_path_buf()),
content_type: None,
@@ -203,7 +206,7 @@ impl Command for Open {
Ok(output
.into_iter()
.flatten()
.into_pipeline_data(call_span, ctrlc))
.into_pipeline_data(call_span, engine_state.signals().clone()))
}
}

View File

@@ -451,12 +451,7 @@ fn rm(
});
for result in iter {
if nu_utils::ctrl_c::was_pressed(&engine_state.ctrlc) {
return Err(ShellError::InterruptedByUser {
span: Some(call.head),
});
}
engine_state.signals().check(call.head)?;
match result {
Ok(None) => {}
Ok(Some(msg)) => eprintln!("{msg}"),

View File

@@ -5,15 +5,14 @@ use nu_engine::{command_prelude::*, current_dir};
use nu_path::expand_path_with;
use nu_protocol::{
ast::{Expr, Expression},
byte_stream::copy_with_interrupt,
byte_stream::copy_with_signals,
process::ChildPipe,
ByteStreamSource, DataSource, OutDest, PipelineMetadata,
ByteStreamSource, DataSource, OutDest, PipelineMetadata, Signals,
};
use std::{
fs::File,
io::{self, BufRead, BufReader, Read, Write},
path::{Path, PathBuf},
sync::{atomic::AtomicBool, Arc},
thread,
};
@@ -120,30 +119,30 @@ impl Command for Save {
)?;
let size = stream.known_size();
let ctrlc = engine_state.ctrlc.clone();
let signals = engine_state.signals();
match stream.into_source() {
ByteStreamSource::Read(read) => {
stream_to_file(read, size, ctrlc, file, span, progress)?;
stream_to_file(read, size, signals, file, span, progress)?;
}
ByteStreamSource::File(source) => {
stream_to_file(source, size, ctrlc, file, span, progress)?;
stream_to_file(source, size, signals, file, span, progress)?;
}
ByteStreamSource::Child(mut child) => {
fn write_or_consume_stderr(
stderr: ChildPipe,
file: Option<File>,
span: Span,
ctrlc: Option<Arc<AtomicBool>>,
signals: &Signals,
progress: bool,
) -> Result<(), ShellError> {
if let Some(file) = file {
match stderr {
ChildPipe::Pipe(pipe) => {
stream_to_file(pipe, None, ctrlc, file, span, progress)
stream_to_file(pipe, None, signals, file, span, progress)
}
ChildPipe::Tee(tee) => {
stream_to_file(tee, None, ctrlc, file, span, progress)
stream_to_file(tee, None, signals, file, span, progress)
}
}?
} else {
@@ -163,14 +162,14 @@ impl Command for Save {
// delegate a thread to redirect stderr to result.
let handler = stderr
.map(|stderr| {
let ctrlc = ctrlc.clone();
let signals = signals.clone();
thread::Builder::new().name("stderr saver".into()).spawn(
move || {
write_or_consume_stderr(
stderr,
stderr_file,
span,
ctrlc,
&signals,
progress,
)
},
@@ -181,10 +180,10 @@ impl Command for Save {
let res = match stdout {
ChildPipe::Pipe(pipe) => {
stream_to_file(pipe, None, ctrlc, file, span, progress)
stream_to_file(pipe, None, signals, file, span, progress)
}
ChildPipe::Tee(tee) => {
stream_to_file(tee, None, ctrlc, file, span, progress)
stream_to_file(tee, None, signals, file, span, progress)
}
};
if let Some(h) = handler {
@@ -202,7 +201,7 @@ impl Command for Save {
stderr,
stderr_file,
span,
ctrlc,
signals,
progress,
)?;
}
@@ -510,7 +509,7 @@ fn get_files(
fn stream_to_file(
source: impl Read,
known_size: Option<u64>,
ctrlc: Option<Arc<AtomicBool>>,
signals: &Signals,
mut file: File,
span: Span,
progress: bool,
@@ -526,9 +525,9 @@ fn stream_to_file(
let mut reader = BufReader::new(source);
let res = loop {
if nu_utils::ctrl_c::was_pressed(&ctrlc) {
if let Err(err) = signals.check(span) {
bar.abandoned_msg("# Cancelled #".to_owned());
return Ok(());
return Err(err);
}
match reader.fill_buf() {
@@ -555,7 +554,7 @@ fn stream_to_file(
Ok(())
}
} else {
copy_with_interrupt(source, file, span, ctrlc.as_deref())?;
copy_with_signals(source, file, span, signals)?;
Ok(())
}
}

View File

@@ -143,7 +143,6 @@ impl Command for Watch {
None => RecursiveMode::Recursive,
};
let ctrlc_ref = &engine_state.ctrlc.clone();
let (tx, rx) = channel();
let mut debouncer = match new_debouncer(debounce_duration, None, tx) {
@@ -256,7 +255,7 @@ impl Command for Watch {
}
Err(RecvTimeoutError::Timeout) => {}
}
if nu_utils::ctrl_c::was_pressed(ctrlc_ref) {
if engine_state.signals().interrupted() {
break;
}
}