mirror of
https://github.com/denoland/deno.git
synced 2025-01-06 22:35:51 -05:00
fix(task): forward signals to spawned sub-processes on unix (#27141)
Closes https://github.com/denoland/deno/issues/18445
This commit is contained in:
parent
8626ec7c25
commit
f6248601f4
15 changed files with 590 additions and 288 deletions
6
Cargo.lock
generated
6
Cargo.lock
generated
|
@ -2136,19 +2136,19 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "deno_task_shell"
|
name = "deno_task_shell"
|
||||||
version = "0.18.1"
|
version = "0.20.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4f444918f7102c1a5a143e9d57809e499fb4d365070519bf2e8bdb16d586af2a"
|
checksum = "01e09966ce29f8d26b652a43355397e1df43b85759e7824196bf0ceaeaa9a2f4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"futures",
|
"futures",
|
||||||
"glob",
|
"glob",
|
||||||
"monch",
|
"monch",
|
||||||
|
"nix",
|
||||||
"os_pipe",
|
"os_pipe",
|
||||||
"path-dedot",
|
"path-dedot",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-util",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
|
@ -82,7 +82,7 @@ deno_path_util.workspace = true
|
||||||
deno_resolver.workspace = true
|
deno_resolver.workspace = true
|
||||||
deno_runtime = { workspace = true, features = ["include_js_files_for_snapshotting"] }
|
deno_runtime = { workspace = true, features = ["include_js_files_for_snapshotting"] }
|
||||||
deno_semver.workspace = true
|
deno_semver.workspace = true
|
||||||
deno_task_shell = "=0.18.1"
|
deno_task_shell = "=0.20.1"
|
||||||
deno_telemetry.workspace = true
|
deno_telemetry.workspace = true
|
||||||
deno_terminal.workspace = true
|
deno_terminal.workspace = true
|
||||||
libsui = "0.5.0"
|
libsui = "0.5.0"
|
||||||
|
|
|
@ -1297,16 +1297,10 @@ impl TsServer {
|
||||||
{
|
{
|
||||||
// When an LSP request is cancelled by the client, the future this is being
|
// When an LSP request is cancelled by the client, the future this is being
|
||||||
// executed under and any local variables here will be dropped at the next
|
// executed under and any local variables here will be dropped at the next
|
||||||
// await point. To pass on that cancellation to the TS thread, we make this
|
// await point. To pass on that cancellation to the TS thread, we use drop_guard
|
||||||
// wrapper which cancels the request's token on drop.
|
// which cancels the request's token on drop.
|
||||||
struct DroppableToken(CancellationToken);
|
|
||||||
impl Drop for DroppableToken {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
self.0.cancel();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let token = token.child_token();
|
let token = token.child_token();
|
||||||
let droppable_token = DroppableToken(token.clone());
|
let droppable_token = token.clone().drop_guard();
|
||||||
let (tx, mut rx) = oneshot::channel::<Result<String, AnyError>>();
|
let (tx, mut rx) = oneshot::channel::<Result<String, AnyError>>();
|
||||||
let change = self.pending_change.lock().take();
|
let change = self.pending_change.lock().take();
|
||||||
|
|
||||||
|
@ -1320,7 +1314,7 @@ impl TsServer {
|
||||||
tokio::select! {
|
tokio::select! {
|
||||||
value = &mut rx => {
|
value = &mut rx => {
|
||||||
let value = value??;
|
let value = value??;
|
||||||
drop(droppable_token);
|
droppable_token.disarm();
|
||||||
Ok(serde_json::from_str(&value)?)
|
Ok(serde_json::from_str(&value)?)
|
||||||
}
|
}
|
||||||
_ = token.cancelled() => {
|
_ = token.cancelled() => {
|
||||||
|
|
|
@ -9,6 +9,7 @@ use deno_npm::resolution::NpmResolutionSnapshot;
|
||||||
use deno_runtime::deno_io::FromRawIoHandle;
|
use deno_runtime::deno_io::FromRawIoHandle;
|
||||||
use deno_semver::package::PackageNv;
|
use deno_semver::package::PackageNv;
|
||||||
use deno_semver::Version;
|
use deno_semver::Version;
|
||||||
|
use deno_task_shell::KillSignal;
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
@ -155,6 +156,29 @@ impl<'a> LifecycleScripts<'a> {
|
||||||
packages: &[NpmResolutionPackage],
|
packages: &[NpmResolutionPackage],
|
||||||
root_node_modules_dir_path: &Path,
|
root_node_modules_dir_path: &Path,
|
||||||
progress_bar: &ProgressBar,
|
progress_bar: &ProgressBar,
|
||||||
|
) -> Result<(), AnyError> {
|
||||||
|
let kill_signal = KillSignal::default();
|
||||||
|
let _drop_signal = kill_signal.clone().drop_guard();
|
||||||
|
// we don't run with signals forwarded because once signals
|
||||||
|
// are setup then they're process wide.
|
||||||
|
self
|
||||||
|
.finish_with_cancellation(
|
||||||
|
snapshot,
|
||||||
|
packages,
|
||||||
|
root_node_modules_dir_path,
|
||||||
|
progress_bar,
|
||||||
|
kill_signal,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn finish_with_cancellation(
|
||||||
|
self,
|
||||||
|
snapshot: &NpmResolutionSnapshot,
|
||||||
|
packages: &[NpmResolutionPackage],
|
||||||
|
root_node_modules_dir_path: &Path,
|
||||||
|
progress_bar: &ProgressBar,
|
||||||
|
kill_signal: KillSignal,
|
||||||
) -> Result<(), AnyError> {
|
) -> Result<(), AnyError> {
|
||||||
self.warn_not_run_scripts()?;
|
self.warn_not_run_scripts()?;
|
||||||
let get_package_path =
|
let get_package_path =
|
||||||
|
@ -246,6 +270,7 @@ impl<'a> LifecycleScripts<'a> {
|
||||||
stderr: TaskStdio::piped(),
|
stderr: TaskStdio::piped(),
|
||||||
stdout: TaskStdio::piped(),
|
stdout: TaskStdio::piped(),
|
||||||
}),
|
}),
|
||||||
|
kill_signal: kill_signal.clone(),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
|
@ -14,6 +14,7 @@ use deno_runtime::deno_node::NodeResolver;
|
||||||
use deno_semver::package::PackageNv;
|
use deno_semver::package::PackageNv;
|
||||||
use deno_task_shell::ExecutableCommand;
|
use deno_task_shell::ExecutableCommand;
|
||||||
use deno_task_shell::ExecuteResult;
|
use deno_task_shell::ExecuteResult;
|
||||||
|
use deno_task_shell::KillSignal;
|
||||||
use deno_task_shell::ShellCommand;
|
use deno_task_shell::ShellCommand;
|
||||||
use deno_task_shell::ShellCommandContext;
|
use deno_task_shell::ShellCommandContext;
|
||||||
use deno_task_shell::ShellPipeReader;
|
use deno_task_shell::ShellPipeReader;
|
||||||
|
@ -22,6 +23,7 @@ use lazy_regex::Lazy;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use tokio::task::JoinHandle;
|
use tokio::task::JoinHandle;
|
||||||
use tokio::task::LocalSet;
|
use tokio::task::LocalSet;
|
||||||
|
use tokio_util::sync::CancellationToken;
|
||||||
|
|
||||||
use crate::npm::CliNpmResolver;
|
use crate::npm::CliNpmResolver;
|
||||||
use crate::npm::InnerCliNpmResolverRef;
|
use crate::npm::InnerCliNpmResolverRef;
|
||||||
|
@ -45,9 +47,11 @@ impl TaskStdio {
|
||||||
pub fn stdout() -> Self {
|
pub fn stdout() -> Self {
|
||||||
Self(None, ShellPipeWriter::stdout())
|
Self(None, ShellPipeWriter::stdout())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn stderr() -> Self {
|
pub fn stderr() -> Self {
|
||||||
Self(None, ShellPipeWriter::stderr())
|
Self(None, ShellPipeWriter::stderr())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn piped() -> Self {
|
pub fn piped() -> Self {
|
||||||
let (r, w) = deno_task_shell::pipe();
|
let (r, w) = deno_task_shell::pipe();
|
||||||
Self(Some(r), w)
|
Self(Some(r), w)
|
||||||
|
@ -62,8 +66,8 @@ pub struct TaskIo {
|
||||||
impl Default for TaskIo {
|
impl Default for TaskIo {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
stderr: TaskStdio::stderr(),
|
|
||||||
stdout: TaskStdio::stdout(),
|
stdout: TaskStdio::stdout(),
|
||||||
|
stderr: TaskStdio::stderr(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -78,6 +82,7 @@ pub struct RunTaskOptions<'a> {
|
||||||
pub custom_commands: HashMap<String, Rc<dyn ShellCommand>>,
|
pub custom_commands: HashMap<String, Rc<dyn ShellCommand>>,
|
||||||
pub root_node_modules_dir: Option<&'a Path>,
|
pub root_node_modules_dir: Option<&'a Path>,
|
||||||
pub stdio: Option<TaskIo>,
|
pub stdio: Option<TaskIo>,
|
||||||
|
pub kill_signal: KillSignal,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type TaskCustomCommands = HashMap<String, Rc<dyn ShellCommand>>;
|
pub type TaskCustomCommands = HashMap<String, Rc<dyn ShellCommand>>;
|
||||||
|
@ -96,8 +101,12 @@ pub async fn run_task(
|
||||||
.with_context(|| format!("Error parsing script '{}'.", opts.task_name))?;
|
.with_context(|| format!("Error parsing script '{}'.", opts.task_name))?;
|
||||||
let env_vars =
|
let env_vars =
|
||||||
prepare_env_vars(opts.env_vars, opts.init_cwd, opts.root_node_modules_dir);
|
prepare_env_vars(opts.env_vars, opts.init_cwd, opts.root_node_modules_dir);
|
||||||
let state =
|
let state = deno_task_shell::ShellState::new(
|
||||||
deno_task_shell::ShellState::new(env_vars, opts.cwd, opts.custom_commands);
|
env_vars,
|
||||||
|
opts.cwd,
|
||||||
|
opts.custom_commands,
|
||||||
|
opts.kill_signal,
|
||||||
|
);
|
||||||
let stdio = opts.stdio.unwrap_or_default();
|
let stdio = opts.stdio.unwrap_or_default();
|
||||||
let (
|
let (
|
||||||
TaskStdio(stdout_read, stdout_write),
|
TaskStdio(stdout_read, stdout_write),
|
||||||
|
@ -537,6 +546,80 @@ fn resolve_managed_npm_commands(
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Runs a deno task future forwarding any signals received
|
||||||
|
/// to the process.
|
||||||
|
///
|
||||||
|
/// Signal listeners and ctrl+c listening will be setup.
|
||||||
|
pub async fn run_future_forwarding_signals<TOutput>(
|
||||||
|
kill_signal: KillSignal,
|
||||||
|
future: impl std::future::Future<Output = TOutput>,
|
||||||
|
) -> TOutput {
|
||||||
|
fn spawn_future_with_cancellation(
|
||||||
|
future: impl std::future::Future<Output = ()> + 'static,
|
||||||
|
token: CancellationToken,
|
||||||
|
) {
|
||||||
|
deno_core::unsync::spawn(async move {
|
||||||
|
tokio::select! {
|
||||||
|
_ = future => {}
|
||||||
|
_ = token.cancelled() => {}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let token = CancellationToken::new();
|
||||||
|
let _token_drop_guard = token.clone().drop_guard();
|
||||||
|
let _drop_guard = kill_signal.clone().drop_guard();
|
||||||
|
|
||||||
|
spawn_future_with_cancellation(
|
||||||
|
listen_ctrl_c(kill_signal.clone()),
|
||||||
|
token.clone(),
|
||||||
|
);
|
||||||
|
#[cfg(unix)]
|
||||||
|
spawn_future_with_cancellation(
|
||||||
|
listen_and_forward_all_signals(kill_signal),
|
||||||
|
token,
|
||||||
|
);
|
||||||
|
|
||||||
|
future.await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn listen_ctrl_c(kill_signal: KillSignal) {
|
||||||
|
while let Ok(()) = tokio::signal::ctrl_c().await {
|
||||||
|
kill_signal.send(deno_task_shell::SignalKind::SIGINT)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
async fn listen_and_forward_all_signals(kill_signal: KillSignal) {
|
||||||
|
use deno_core::futures::FutureExt;
|
||||||
|
use deno_runtime::signal::SIGNAL_NUMS;
|
||||||
|
|
||||||
|
// listen and forward every signal we support
|
||||||
|
let mut futures = Vec::with_capacity(SIGNAL_NUMS.len());
|
||||||
|
for signo in SIGNAL_NUMS.iter().copied() {
|
||||||
|
if signo == libc::SIGKILL || signo == libc::SIGSTOP {
|
||||||
|
continue; // skip, can't listen to these
|
||||||
|
}
|
||||||
|
|
||||||
|
let kill_signal = kill_signal.clone();
|
||||||
|
futures.push(
|
||||||
|
async move {
|
||||||
|
let Ok(mut stream) = tokio::signal::unix::signal(
|
||||||
|
tokio::signal::unix::SignalKind::from_raw(signo),
|
||||||
|
) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let signal_kind: deno_task_shell::SignalKind = signo.into();
|
||||||
|
while let Some(()) = stream.recv().await {
|
||||||
|
kill_signal.send(signal_kind);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.boxed_local(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
futures::future::join_all(futures).await;
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,7 @@ use deno_core::futures::StreamExt;
|
||||||
use deno_core::url::Url;
|
use deno_core::url::Url;
|
||||||
use deno_path_util::normalize_path;
|
use deno_path_util::normalize_path;
|
||||||
use deno_runtime::deno_node::NodeResolver;
|
use deno_runtime::deno_node::NodeResolver;
|
||||||
|
use deno_task_shell::KillSignal;
|
||||||
use deno_task_shell::ShellCommand;
|
use deno_task_shell::ShellCommand;
|
||||||
use indexmap::IndexMap;
|
use indexmap::IndexMap;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
|
@ -37,6 +38,7 @@ use crate::colors;
|
||||||
use crate::factory::CliFactory;
|
use crate::factory::CliFactory;
|
||||||
use crate::npm::CliNpmResolver;
|
use crate::npm::CliNpmResolver;
|
||||||
use crate::task_runner;
|
use crate::task_runner;
|
||||||
|
use crate::task_runner::run_future_forwarding_signals;
|
||||||
use crate::util::fs::canonicalize_path;
|
use crate::util::fs::canonicalize_path;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -226,28 +228,33 @@ pub async fn execute_script(
|
||||||
concurrency: no_of_concurrent_tasks.into(),
|
concurrency: no_of_concurrent_tasks.into(),
|
||||||
};
|
};
|
||||||
|
|
||||||
if task_flags.eval {
|
let kill_signal = KillSignal::default();
|
||||||
return task_runner
|
run_future_forwarding_signals(kill_signal.clone(), async {
|
||||||
.run_deno_task(
|
if task_flags.eval {
|
||||||
&Url::from_directory_path(cli_options.initial_cwd()).unwrap(),
|
return task_runner
|
||||||
"",
|
.run_deno_task(
|
||||||
&TaskDefinition {
|
&Url::from_directory_path(cli_options.initial_cwd()).unwrap(),
|
||||||
command: task_flags.task.as_ref().unwrap().to_string(),
|
"",
|
||||||
dependencies: vec![],
|
&TaskDefinition {
|
||||||
description: None,
|
command: task_flags.task.as_ref().unwrap().to_string(),
|
||||||
},
|
dependencies: vec![],
|
||||||
)
|
description: None,
|
||||||
.await;
|
},
|
||||||
}
|
kill_signal,
|
||||||
|
)
|
||||||
for task_config in &packages_task_configs {
|
.await;
|
||||||
let exit_code = task_runner.run_tasks(task_config).await?;
|
|
||||||
if exit_code > 0 {
|
|
||||||
return Ok(exit_code);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
Ok(0)
|
for task_config in &packages_task_configs {
|
||||||
|
let exit_code = task_runner.run_tasks(task_config, &kill_signal).await?;
|
||||||
|
if exit_code > 0 {
|
||||||
|
return Ok(exit_code);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(0)
|
||||||
|
})
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
struct RunSingleOptions<'a> {
|
struct RunSingleOptions<'a> {
|
||||||
|
@ -255,6 +262,7 @@ struct RunSingleOptions<'a> {
|
||||||
script: &'a str,
|
script: &'a str,
|
||||||
cwd: &'a Path,
|
cwd: &'a Path,
|
||||||
custom_commands: HashMap<String, Rc<dyn ShellCommand>>,
|
custom_commands: HashMap<String, Rc<dyn ShellCommand>>,
|
||||||
|
kill_signal: KillSignal,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct TaskRunner<'a> {
|
struct TaskRunner<'a> {
|
||||||
|
@ -270,9 +278,10 @@ impl<'a> TaskRunner<'a> {
|
||||||
pub async fn run_tasks(
|
pub async fn run_tasks(
|
||||||
&self,
|
&self,
|
||||||
pkg_tasks_config: &PackageTaskInfo,
|
pkg_tasks_config: &PackageTaskInfo,
|
||||||
|
kill_signal: &KillSignal,
|
||||||
) -> Result<i32, deno_core::anyhow::Error> {
|
) -> Result<i32, deno_core::anyhow::Error> {
|
||||||
match sort_tasks_topo(pkg_tasks_config) {
|
match sort_tasks_topo(pkg_tasks_config) {
|
||||||
Ok(sorted) => self.run_tasks_in_parallel(sorted).await,
|
Ok(sorted) => self.run_tasks_in_parallel(sorted, kill_signal).await,
|
||||||
Err(err) => match err {
|
Err(err) => match err {
|
||||||
TaskError::NotFound(name) => {
|
TaskError::NotFound(name) => {
|
||||||
if self.task_flags.is_run {
|
if self.task_flags.is_run {
|
||||||
|
@ -307,6 +316,7 @@ impl<'a> TaskRunner<'a> {
|
||||||
async fn run_tasks_in_parallel(
|
async fn run_tasks_in_parallel(
|
||||||
&self,
|
&self,
|
||||||
tasks: Vec<ResolvedTask<'a>>,
|
tasks: Vec<ResolvedTask<'a>>,
|
||||||
|
kill_signal: &KillSignal,
|
||||||
) -> Result<i32, deno_core::anyhow::Error> {
|
) -> Result<i32, deno_core::anyhow::Error> {
|
||||||
struct PendingTasksContext<'a> {
|
struct PendingTasksContext<'a> {
|
||||||
completed: HashSet<usize>,
|
completed: HashSet<usize>,
|
||||||
|
@ -327,6 +337,7 @@ impl<'a> TaskRunner<'a> {
|
||||||
fn get_next_task<'b>(
|
fn get_next_task<'b>(
|
||||||
&mut self,
|
&mut self,
|
||||||
runner: &'b TaskRunner<'b>,
|
runner: &'b TaskRunner<'b>,
|
||||||
|
kill_signal: &KillSignal,
|
||||||
) -> Option<
|
) -> Option<
|
||||||
LocalBoxFuture<'b, Result<(i32, &'a ResolvedTask<'a>), AnyError>>,
|
LocalBoxFuture<'b, Result<(i32, &'a ResolvedTask<'a>), AnyError>>,
|
||||||
>
|
>
|
||||||
|
@ -349,15 +360,23 @@ impl<'a> TaskRunner<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
self.running.insert(task.id);
|
self.running.insert(task.id);
|
||||||
|
let kill_signal = kill_signal.clone();
|
||||||
return Some(
|
return Some(
|
||||||
async move {
|
async move {
|
||||||
match task.task_or_script {
|
match task.task_or_script {
|
||||||
TaskOrScript::Task(_, def) => {
|
TaskOrScript::Task(_, def) => {
|
||||||
runner.run_deno_task(task.folder_url, task.name, def).await
|
runner
|
||||||
|
.run_deno_task(task.folder_url, task.name, def, kill_signal)
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
TaskOrScript::Script(scripts, _) => {
|
TaskOrScript::Script(scripts, _) => {
|
||||||
runner
|
runner
|
||||||
.run_npm_script(task.folder_url, task.name, scripts)
|
.run_npm_script(
|
||||||
|
task.folder_url,
|
||||||
|
task.name,
|
||||||
|
scripts,
|
||||||
|
kill_signal,
|
||||||
|
)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -380,7 +399,7 @@ impl<'a> TaskRunner<'a> {
|
||||||
|
|
||||||
while context.has_remaining_tasks() {
|
while context.has_remaining_tasks() {
|
||||||
while queue.len() < self.concurrency {
|
while queue.len() < self.concurrency {
|
||||||
if let Some(task) = context.get_next_task(self) {
|
if let Some(task) = context.get_next_task(self, kill_signal) {
|
||||||
queue.push(task);
|
queue.push(task);
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
|
@ -409,6 +428,7 @@ impl<'a> TaskRunner<'a> {
|
||||||
dir_url: &Url,
|
dir_url: &Url,
|
||||||
task_name: &str,
|
task_name: &str,
|
||||||
definition: &TaskDefinition,
|
definition: &TaskDefinition,
|
||||||
|
kill_signal: KillSignal,
|
||||||
) -> Result<i32, deno_core::anyhow::Error> {
|
) -> Result<i32, deno_core::anyhow::Error> {
|
||||||
let cwd = match &self.task_flags.cwd {
|
let cwd = match &self.task_flags.cwd {
|
||||||
Some(path) => canonicalize_path(&PathBuf::from(path))
|
Some(path) => canonicalize_path(&PathBuf::from(path))
|
||||||
|
@ -426,6 +446,7 @@ impl<'a> TaskRunner<'a> {
|
||||||
script: &definition.command,
|
script: &definition.command,
|
||||||
cwd: &cwd,
|
cwd: &cwd,
|
||||||
custom_commands,
|
custom_commands,
|
||||||
|
kill_signal,
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
@ -435,6 +456,7 @@ impl<'a> TaskRunner<'a> {
|
||||||
dir_url: &Url,
|
dir_url: &Url,
|
||||||
task_name: &str,
|
task_name: &str,
|
||||||
scripts: &IndexMap<String, String>,
|
scripts: &IndexMap<String, String>,
|
||||||
|
kill_signal: KillSignal,
|
||||||
) -> Result<i32, deno_core::anyhow::Error> {
|
) -> Result<i32, deno_core::anyhow::Error> {
|
||||||
// ensure the npm packages are installed if using a managed resolver
|
// ensure the npm packages are installed if using a managed resolver
|
||||||
if let Some(npm_resolver) = self.npm_resolver.as_managed() {
|
if let Some(npm_resolver) = self.npm_resolver.as_managed() {
|
||||||
|
@ -466,6 +488,7 @@ impl<'a> TaskRunner<'a> {
|
||||||
script,
|
script,
|
||||||
cwd: &cwd,
|
cwd: &cwd,
|
||||||
custom_commands: custom_commands.clone(),
|
custom_commands: custom_commands.clone(),
|
||||||
|
kill_signal: kill_signal.clone(),
|
||||||
})
|
})
|
||||||
.await?;
|
.await?;
|
||||||
if exit_code > 0 {
|
if exit_code > 0 {
|
||||||
|
@ -486,6 +509,7 @@ impl<'a> TaskRunner<'a> {
|
||||||
script,
|
script,
|
||||||
cwd,
|
cwd,
|
||||||
custom_commands,
|
custom_commands,
|
||||||
|
kill_signal,
|
||||||
} = opts;
|
} = opts;
|
||||||
|
|
||||||
output_task(
|
output_task(
|
||||||
|
@ -504,6 +528,7 @@ impl<'a> TaskRunner<'a> {
|
||||||
argv: self.cli_options.argv(),
|
argv: self.cli_options.argv(),
|
||||||
root_node_modules_dir: self.npm_resolver.root_node_modules_path(),
|
root_node_modules_dir: self.npm_resolver.root_node_modules_path(),
|
||||||
stdio: None,
|
stdio: None,
|
||||||
|
kill_signal,
|
||||||
})
|
})
|
||||||
.await?
|
.await?
|
||||||
.exit_code,
|
.exit_code,
|
||||||
|
|
|
@ -34,6 +34,7 @@ pub mod inspector_server;
|
||||||
pub mod js;
|
pub mod js;
|
||||||
pub mod ops;
|
pub mod ops;
|
||||||
pub mod permissions;
|
pub mod permissions;
|
||||||
|
pub mod signal;
|
||||||
pub mod snapshot;
|
pub mod snapshot;
|
||||||
pub mod sys_info;
|
pub mod sys_info;
|
||||||
pub mod tokio_util;
|
pub mod tokio_util;
|
||||||
|
|
|
@ -256,9 +256,7 @@ impl TryFrom<ExitStatus> for ChildStatus {
|
||||||
success: false,
|
success: false,
|
||||||
code: 128 + signal,
|
code: 128 + signal,
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
signal: Some(
|
signal: Some(crate::signal::signal_int_to_str(signal)?.to_string()),
|
||||||
crate::ops::signal::signal_int_to_str(signal)?.to_string(),
|
|
||||||
),
|
|
||||||
#[cfg(not(unix))]
|
#[cfg(not(unix))]
|
||||||
signal: None,
|
signal: None,
|
||||||
}
|
}
|
||||||
|
@ -1076,7 +1074,8 @@ mod deprecated {
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
pub fn kill(pid: i32, signal: &str) -> Result<(), ProcessError> {
|
pub fn kill(pid: i32, signal: &str) -> Result<(), ProcessError> {
|
||||||
let signo = super::super::signal::signal_str_to_int(signal)?;
|
let signo = crate::signal::signal_str_to_int(signal)
|
||||||
|
.map_err(SignalError::InvalidSignalStr)?;
|
||||||
use nix::sys::signal::kill as unix_kill;
|
use nix::sys::signal::kill as unix_kill;
|
||||||
use nix::sys::signal::Signal;
|
use nix::sys::signal::Signal;
|
||||||
use nix::unistd::Pid;
|
use nix::unistd::Pid;
|
||||||
|
@ -1099,7 +1098,12 @@ mod deprecated {
|
||||||
use winapi::um::winnt::PROCESS_TERMINATE;
|
use winapi::um::winnt::PROCESS_TERMINATE;
|
||||||
|
|
||||||
if !matches!(signal, "SIGKILL" | "SIGTERM") {
|
if !matches!(signal, "SIGKILL" | "SIGTERM") {
|
||||||
Err(SignalError::InvalidSignalStr(signal.to_string()).into())
|
Err(
|
||||||
|
SignalError::InvalidSignalStr(crate::signal::InvalidSignalStrError(
|
||||||
|
signal.to_string(),
|
||||||
|
))
|
||||||
|
.into(),
|
||||||
|
)
|
||||||
} else if pid <= 0 {
|
} else if pid <= 0 {
|
||||||
Err(ProcessError::InvalidPid)
|
Err(ProcessError::InvalidPid)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -46,34 +46,10 @@ deno_core::extension!(
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
pub enum SignalError {
|
pub enum SignalError {
|
||||||
#[cfg(any(
|
#[error(transparent)]
|
||||||
target_os = "android",
|
InvalidSignalStr(#[from] crate::signal::InvalidSignalStrError),
|
||||||
target_os = "linux",
|
#[error(transparent)]
|
||||||
target_os = "openbsd",
|
InvalidSignalInt(#[from] crate::signal::InvalidSignalIntError),
|
||||||
target_os = "openbsd",
|
|
||||||
target_os = "macos",
|
|
||||||
target_os = "solaris",
|
|
||||||
target_os = "illumos"
|
|
||||||
))]
|
|
||||||
#[error("Invalid signal: {0}")]
|
|
||||||
InvalidSignalStr(String),
|
|
||||||
#[cfg(any(
|
|
||||||
target_os = "android",
|
|
||||||
target_os = "linux",
|
|
||||||
target_os = "openbsd",
|
|
||||||
target_os = "openbsd",
|
|
||||||
target_os = "macos",
|
|
||||||
target_os = "solaris",
|
|
||||||
target_os = "illumos"
|
|
||||||
))]
|
|
||||||
#[error("Invalid signal: {0}")]
|
|
||||||
InvalidSignalInt(libc::c_int),
|
|
||||||
#[cfg(target_os = "windows")]
|
|
||||||
#[error("Windows only supports ctrl-c (SIGINT) and ctrl-break (SIGBREAK), but got {0}")]
|
|
||||||
InvalidSignalStr(String),
|
|
||||||
#[cfg(target_os = "windows")]
|
|
||||||
#[error("Windows only supports ctrl-c (SIGINT) and ctrl-break (SIGBREAK), but got {0}")]
|
|
||||||
InvalidSignalInt(libc::c_int),
|
|
||||||
#[error("Binding to signal '{0}' is not allowed")]
|
#[error("Binding to signal '{0}' is not allowed")]
|
||||||
SignalNotAllowed(String),
|
SignalNotAllowed(String),
|
||||||
#[error("{0}")]
|
#[error("{0}")]
|
||||||
|
@ -181,218 +157,6 @@ impl Resource for SignalStreamResource {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! first_literal {
|
|
||||||
($head:literal $(, $tail:literal)*) => {
|
|
||||||
$head
|
|
||||||
};
|
|
||||||
}
|
|
||||||
macro_rules! signal_dict {
|
|
||||||
($(($number:literal, $($name:literal)|+)),*) => {
|
|
||||||
pub fn signal_str_to_int(s: &str) -> Result<libc::c_int, SignalError> {
|
|
||||||
match s {
|
|
||||||
$($($name)|* => Ok($number),)*
|
|
||||||
_ => Err(SignalError::InvalidSignalStr(s.to_string())),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn signal_int_to_str(s: libc::c_int) -> Result<&'static str, SignalError> {
|
|
||||||
match s {
|
|
||||||
$($number => Ok(first_literal!($($name),+)),)*
|
|
||||||
_ => Err(SignalError::InvalidSignalInt(s)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(target_os = "freebsd")]
|
|
||||||
signal_dict!(
|
|
||||||
(1, "SIGHUP"),
|
|
||||||
(2, "SIGINT"),
|
|
||||||
(3, "SIGQUIT"),
|
|
||||||
(4, "SIGILL"),
|
|
||||||
(5, "SIGTRAP"),
|
|
||||||
(6, "SIGABRT" | "SIGIOT"),
|
|
||||||
(7, "SIGEMT"),
|
|
||||||
(8, "SIGFPE"),
|
|
||||||
(9, "SIGKILL"),
|
|
||||||
(10, "SIGBUS"),
|
|
||||||
(11, "SIGSEGV"),
|
|
||||||
(12, "SIGSYS"),
|
|
||||||
(13, "SIGPIPE"),
|
|
||||||
(14, "SIGALRM"),
|
|
||||||
(15, "SIGTERM"),
|
|
||||||
(16, "SIGURG"),
|
|
||||||
(17, "SIGSTOP"),
|
|
||||||
(18, "SIGTSTP"),
|
|
||||||
(19, "SIGCONT"),
|
|
||||||
(20, "SIGCHLD"),
|
|
||||||
(21, "SIGTTIN"),
|
|
||||||
(22, "SIGTTOU"),
|
|
||||||
(23, "SIGIO"),
|
|
||||||
(24, "SIGXCPU"),
|
|
||||||
(25, "SIGXFSZ"),
|
|
||||||
(26, "SIGVTALRM"),
|
|
||||||
(27, "SIGPROF"),
|
|
||||||
(28, "SIGWINCH"),
|
|
||||||
(29, "SIGINFO"),
|
|
||||||
(30, "SIGUSR1"),
|
|
||||||
(31, "SIGUSR2"),
|
|
||||||
(32, "SIGTHR"),
|
|
||||||
(33, "SIGLIBRT")
|
|
||||||
);
|
|
||||||
|
|
||||||
#[cfg(target_os = "openbsd")]
|
|
||||||
signal_dict!(
|
|
||||||
(1, "SIGHUP"),
|
|
||||||
(2, "SIGINT"),
|
|
||||||
(3, "SIGQUIT"),
|
|
||||||
(4, "SIGILL"),
|
|
||||||
(5, "SIGTRAP"),
|
|
||||||
(6, "SIGABRT" | "SIGIOT"),
|
|
||||||
(7, "SIGEMT"),
|
|
||||||
(8, "SIGKILL"),
|
|
||||||
(10, "SIGBUS"),
|
|
||||||
(11, "SIGSEGV"),
|
|
||||||
(12, "SIGSYS"),
|
|
||||||
(13, "SIGPIPE"),
|
|
||||||
(14, "SIGALRM"),
|
|
||||||
(15, "SIGTERM"),
|
|
||||||
(16, "SIGURG"),
|
|
||||||
(17, "SIGSTOP"),
|
|
||||||
(18, "SIGTSTP"),
|
|
||||||
(19, "SIGCONT"),
|
|
||||||
(20, "SIGCHLD"),
|
|
||||||
(21, "SIGTTIN"),
|
|
||||||
(22, "SIGTTOU"),
|
|
||||||
(23, "SIGIO"),
|
|
||||||
(24, "SIGXCPU"),
|
|
||||||
(25, "SIGXFSZ"),
|
|
||||||
(26, "SIGVTALRM"),
|
|
||||||
(27, "SIGPROF"),
|
|
||||||
(28, "SIGWINCH"),
|
|
||||||
(29, "SIGINFO"),
|
|
||||||
(30, "SIGUSR1"),
|
|
||||||
(31, "SIGUSR2"),
|
|
||||||
(32, "SIGTHR")
|
|
||||||
);
|
|
||||||
|
|
||||||
#[cfg(any(target_os = "android", target_os = "linux"))]
|
|
||||||
signal_dict!(
|
|
||||||
(1, "SIGHUP"),
|
|
||||||
(2, "SIGINT"),
|
|
||||||
(3, "SIGQUIT"),
|
|
||||||
(4, "SIGILL"),
|
|
||||||
(5, "SIGTRAP"),
|
|
||||||
(6, "SIGABRT" | "SIGIOT"),
|
|
||||||
(7, "SIGBUS"),
|
|
||||||
(8, "SIGFPE"),
|
|
||||||
(9, "SIGKILL"),
|
|
||||||
(10, "SIGUSR1"),
|
|
||||||
(11, "SIGSEGV"),
|
|
||||||
(12, "SIGUSR2"),
|
|
||||||
(13, "SIGPIPE"),
|
|
||||||
(14, "SIGALRM"),
|
|
||||||
(15, "SIGTERM"),
|
|
||||||
(16, "SIGSTKFLT"),
|
|
||||||
(17, "SIGCHLD"),
|
|
||||||
(18, "SIGCONT"),
|
|
||||||
(19, "SIGSTOP"),
|
|
||||||
(20, "SIGTSTP"),
|
|
||||||
(21, "SIGTTIN"),
|
|
||||||
(22, "SIGTTOU"),
|
|
||||||
(23, "SIGURG"),
|
|
||||||
(24, "SIGXCPU"),
|
|
||||||
(25, "SIGXFSZ"),
|
|
||||||
(26, "SIGVTALRM"),
|
|
||||||
(27, "SIGPROF"),
|
|
||||||
(28, "SIGWINCH"),
|
|
||||||
(29, "SIGIO" | "SIGPOLL"),
|
|
||||||
(30, "SIGPWR"),
|
|
||||||
(31, "SIGSYS" | "SIGUNUSED")
|
|
||||||
);
|
|
||||||
|
|
||||||
#[cfg(target_os = "macos")]
|
|
||||||
signal_dict!(
|
|
||||||
(1, "SIGHUP"),
|
|
||||||
(2, "SIGINT"),
|
|
||||||
(3, "SIGQUIT"),
|
|
||||||
(4, "SIGILL"),
|
|
||||||
(5, "SIGTRAP"),
|
|
||||||
(6, "SIGABRT" | "SIGIOT"),
|
|
||||||
(7, "SIGEMT"),
|
|
||||||
(8, "SIGFPE"),
|
|
||||||
(9, "SIGKILL"),
|
|
||||||
(10, "SIGBUS"),
|
|
||||||
(11, "SIGSEGV"),
|
|
||||||
(12, "SIGSYS"),
|
|
||||||
(13, "SIGPIPE"),
|
|
||||||
(14, "SIGALRM"),
|
|
||||||
(15, "SIGTERM"),
|
|
||||||
(16, "SIGURG"),
|
|
||||||
(17, "SIGSTOP"),
|
|
||||||
(18, "SIGTSTP"),
|
|
||||||
(19, "SIGCONT"),
|
|
||||||
(20, "SIGCHLD"),
|
|
||||||
(21, "SIGTTIN"),
|
|
||||||
(22, "SIGTTOU"),
|
|
||||||
(23, "SIGIO"),
|
|
||||||
(24, "SIGXCPU"),
|
|
||||||
(25, "SIGXFSZ"),
|
|
||||||
(26, "SIGVTALRM"),
|
|
||||||
(27, "SIGPROF"),
|
|
||||||
(28, "SIGWINCH"),
|
|
||||||
(29, "SIGINFO"),
|
|
||||||
(30, "SIGUSR1"),
|
|
||||||
(31, "SIGUSR2")
|
|
||||||
);
|
|
||||||
|
|
||||||
#[cfg(any(target_os = "solaris", target_os = "illumos"))]
|
|
||||||
signal_dict!(
|
|
||||||
(1, "SIGHUP"),
|
|
||||||
(2, "SIGINT"),
|
|
||||||
(3, "SIGQUIT"),
|
|
||||||
(4, "SIGILL"),
|
|
||||||
(5, "SIGTRAP"),
|
|
||||||
(6, "SIGABRT" | "SIGIOT"),
|
|
||||||
(7, "SIGEMT"),
|
|
||||||
(8, "SIGFPE"),
|
|
||||||
(9, "SIGKILL"),
|
|
||||||
(10, "SIGBUS"),
|
|
||||||
(11, "SIGSEGV"),
|
|
||||||
(12, "SIGSYS"),
|
|
||||||
(13, "SIGPIPE"),
|
|
||||||
(14, "SIGALRM"),
|
|
||||||
(15, "SIGTERM"),
|
|
||||||
(16, "SIGUSR1"),
|
|
||||||
(17, "SIGUSR2"),
|
|
||||||
(18, "SIGCHLD"),
|
|
||||||
(19, "SIGPWR"),
|
|
||||||
(20, "SIGWINCH"),
|
|
||||||
(21, "SIGURG"),
|
|
||||||
(22, "SIGPOLL"),
|
|
||||||
(23, "SIGSTOP"),
|
|
||||||
(24, "SIGTSTP"),
|
|
||||||
(25, "SIGCONT"),
|
|
||||||
(26, "SIGTTIN"),
|
|
||||||
(27, "SIGTTOU"),
|
|
||||||
(28, "SIGVTALRM"),
|
|
||||||
(29, "SIGPROF"),
|
|
||||||
(30, "SIGXCPU"),
|
|
||||||
(31, "SIGXFSZ"),
|
|
||||||
(32, "SIGWAITING"),
|
|
||||||
(33, "SIGLWP"),
|
|
||||||
(34, "SIGFREEZE"),
|
|
||||||
(35, "SIGTHAW"),
|
|
||||||
(36, "SIGCANCEL"),
|
|
||||||
(37, "SIGLOST"),
|
|
||||||
(38, "SIGXRES"),
|
|
||||||
(39, "SIGJVM1"),
|
|
||||||
(40, "SIGJVM2")
|
|
||||||
);
|
|
||||||
|
|
||||||
#[cfg(target_os = "windows")]
|
|
||||||
signal_dict!((2, "SIGINT"), (21, "SIGBREAK"));
|
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
#[op2(fast)]
|
#[op2(fast)]
|
||||||
#[smi]
|
#[smi]
|
||||||
|
@ -400,7 +164,7 @@ fn op_signal_bind(
|
||||||
state: &mut OpState,
|
state: &mut OpState,
|
||||||
#[string] sig: &str,
|
#[string] sig: &str,
|
||||||
) -> Result<ResourceId, SignalError> {
|
) -> Result<ResourceId, SignalError> {
|
||||||
let signo = signal_str_to_int(sig)?;
|
let signo = crate::signal::signal_str_to_int(sig)?;
|
||||||
if signal_hook_registry::FORBIDDEN.contains(&signo) {
|
if signal_hook_registry::FORBIDDEN.contains(&signo) {
|
||||||
return Err(SignalError::SignalNotAllowed(sig.to_string()));
|
return Err(SignalError::SignalNotAllowed(sig.to_string()));
|
||||||
}
|
}
|
||||||
|
@ -437,7 +201,7 @@ fn op_signal_bind(
|
||||||
state: &mut OpState,
|
state: &mut OpState,
|
||||||
#[string] sig: &str,
|
#[string] sig: &str,
|
||||||
) -> Result<ResourceId, SignalError> {
|
) -> Result<ResourceId, SignalError> {
|
||||||
let signo = signal_str_to_int(sig)?;
|
let signo = crate::signal::signal_str_to_int(sig)?;
|
||||||
let resource = SignalStreamResource {
|
let resource = SignalStreamResource {
|
||||||
signal: AsyncRefCell::new(match signo {
|
signal: AsyncRefCell::new(match signo {
|
||||||
// SIGINT
|
// SIGINT
|
||||||
|
|
257
runtime/signal.rs
Normal file
257
runtime/signal.rs
Normal file
|
@ -0,0 +1,257 @@
|
||||||
|
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||||
|
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
#[error("Windows only supports ctrl-c (SIGINT) and ctrl-break (SIGBREAK), but got {0}")]
|
||||||
|
pub struct InvalidSignalStrError(pub String);
|
||||||
|
|
||||||
|
#[cfg(any(
|
||||||
|
target_os = "android",
|
||||||
|
target_os = "linux",
|
||||||
|
target_os = "openbsd",
|
||||||
|
target_os = "openbsd",
|
||||||
|
target_os = "macos",
|
||||||
|
target_os = "solaris",
|
||||||
|
target_os = "illumos"
|
||||||
|
))]
|
||||||
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
#[error("Invalid signal: {0}")]
|
||||||
|
pub struct InvalidSignalStrError(pub String);
|
||||||
|
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
#[error("Windows only supports ctrl-c (SIGINT) and ctrl-break (SIGBREAK), but got {0}")]
|
||||||
|
pub struct InvalidSignalIntError(pub libc::c_int);
|
||||||
|
|
||||||
|
#[cfg(any(
|
||||||
|
target_os = "android",
|
||||||
|
target_os = "linux",
|
||||||
|
target_os = "openbsd",
|
||||||
|
target_os = "openbsd",
|
||||||
|
target_os = "macos",
|
||||||
|
target_os = "solaris",
|
||||||
|
target_os = "illumos"
|
||||||
|
))]
|
||||||
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
#[error("Invalid signal: {0}")]
|
||||||
|
pub struct InvalidSignalIntError(pub libc::c_int);
|
||||||
|
|
||||||
|
macro_rules! first_literal {
|
||||||
|
($head:literal $(, $tail:literal)*) => {
|
||||||
|
$head
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! signal_dict {
|
||||||
|
($(($number:literal, $($name:literal)|+)),*) => {
|
||||||
|
|
||||||
|
pub const SIGNAL_NUMS: &'static [libc::c_int] = &[
|
||||||
|
$(
|
||||||
|
$number
|
||||||
|
),*
|
||||||
|
];
|
||||||
|
|
||||||
|
pub fn signal_str_to_int(s: &str) -> Result<libc::c_int, InvalidSignalStrError> {
|
||||||
|
match s {
|
||||||
|
$($($name)|* => Ok($number),)*
|
||||||
|
_ => Err(InvalidSignalStrError(s.to_string())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn signal_int_to_str(s: libc::c_int) -> Result<&'static str, InvalidSignalIntError> {
|
||||||
|
match s {
|
||||||
|
$($number => Ok(first_literal!($($name),+)),)*
|
||||||
|
_ => Err(InvalidSignalIntError(s)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "freebsd")]
|
||||||
|
signal_dict!(
|
||||||
|
(1, "SIGHUP"),
|
||||||
|
(2, "SIGINT"),
|
||||||
|
(3, "SIGQUIT"),
|
||||||
|
(4, "SIGILL"),
|
||||||
|
(5, "SIGTRAP"),
|
||||||
|
(6, "SIGABRT" | "SIGIOT"),
|
||||||
|
(7, "SIGEMT"),
|
||||||
|
(8, "SIGFPE"),
|
||||||
|
(9, "SIGKILL"),
|
||||||
|
(10, "SIGBUS"),
|
||||||
|
(11, "SIGSEGV"),
|
||||||
|
(12, "SIGSYS"),
|
||||||
|
(13, "SIGPIPE"),
|
||||||
|
(14, "SIGALRM"),
|
||||||
|
(15, "SIGTERM"),
|
||||||
|
(16, "SIGURG"),
|
||||||
|
(17, "SIGSTOP"),
|
||||||
|
(18, "SIGTSTP"),
|
||||||
|
(19, "SIGCONT"),
|
||||||
|
(20, "SIGCHLD"),
|
||||||
|
(21, "SIGTTIN"),
|
||||||
|
(22, "SIGTTOU"),
|
||||||
|
(23, "SIGIO"),
|
||||||
|
(24, "SIGXCPU"),
|
||||||
|
(25, "SIGXFSZ"),
|
||||||
|
(26, "SIGVTALRM"),
|
||||||
|
(27, "SIGPROF"),
|
||||||
|
(28, "SIGWINCH"),
|
||||||
|
(29, "SIGINFO"),
|
||||||
|
(30, "SIGUSR1"),
|
||||||
|
(31, "SIGUSR2"),
|
||||||
|
(32, "SIGTHR"),
|
||||||
|
(33, "SIGLIBRT")
|
||||||
|
);
|
||||||
|
|
||||||
|
#[cfg(target_os = "openbsd")]
|
||||||
|
signal_dict!(
|
||||||
|
(1, "SIGHUP"),
|
||||||
|
(2, "SIGINT"),
|
||||||
|
(3, "SIGQUIT"),
|
||||||
|
(4, "SIGILL"),
|
||||||
|
(5, "SIGTRAP"),
|
||||||
|
(6, "SIGABRT" | "SIGIOT"),
|
||||||
|
(7, "SIGEMT"),
|
||||||
|
(8, "SIGKILL"),
|
||||||
|
(10, "SIGBUS"),
|
||||||
|
(11, "SIGSEGV"),
|
||||||
|
(12, "SIGSYS"),
|
||||||
|
(13, "SIGPIPE"),
|
||||||
|
(14, "SIGALRM"),
|
||||||
|
(15, "SIGTERM"),
|
||||||
|
(16, "SIGURG"),
|
||||||
|
(17, "SIGSTOP"),
|
||||||
|
(18, "SIGTSTP"),
|
||||||
|
(19, "SIGCONT"),
|
||||||
|
(20, "SIGCHLD"),
|
||||||
|
(21, "SIGTTIN"),
|
||||||
|
(22, "SIGTTOU"),
|
||||||
|
(23, "SIGIO"),
|
||||||
|
(24, "SIGXCPU"),
|
||||||
|
(25, "SIGXFSZ"),
|
||||||
|
(26, "SIGVTALRM"),
|
||||||
|
(27, "SIGPROF"),
|
||||||
|
(28, "SIGWINCH"),
|
||||||
|
(29, "SIGINFO"),
|
||||||
|
(30, "SIGUSR1"),
|
||||||
|
(31, "SIGUSR2"),
|
||||||
|
(32, "SIGTHR")
|
||||||
|
);
|
||||||
|
|
||||||
|
#[cfg(any(target_os = "android", target_os = "linux"))]
|
||||||
|
signal_dict!(
|
||||||
|
(1, "SIGHUP"),
|
||||||
|
(2, "SIGINT"),
|
||||||
|
(3, "SIGQUIT"),
|
||||||
|
(4, "SIGILL"),
|
||||||
|
(5, "SIGTRAP"),
|
||||||
|
(6, "SIGABRT" | "SIGIOT"),
|
||||||
|
(7, "SIGBUS"),
|
||||||
|
(8, "SIGFPE"),
|
||||||
|
(9, "SIGKILL"),
|
||||||
|
(10, "SIGUSR1"),
|
||||||
|
(11, "SIGSEGV"),
|
||||||
|
(12, "SIGUSR2"),
|
||||||
|
(13, "SIGPIPE"),
|
||||||
|
(14, "SIGALRM"),
|
||||||
|
(15, "SIGTERM"),
|
||||||
|
(16, "SIGSTKFLT"),
|
||||||
|
(17, "SIGCHLD"),
|
||||||
|
(18, "SIGCONT"),
|
||||||
|
(19, "SIGSTOP"),
|
||||||
|
(20, "SIGTSTP"),
|
||||||
|
(21, "SIGTTIN"),
|
||||||
|
(22, "SIGTTOU"),
|
||||||
|
(23, "SIGURG"),
|
||||||
|
(24, "SIGXCPU"),
|
||||||
|
(25, "SIGXFSZ"),
|
||||||
|
(26, "SIGVTALRM"),
|
||||||
|
(27, "SIGPROF"),
|
||||||
|
(28, "SIGWINCH"),
|
||||||
|
(29, "SIGIO" | "SIGPOLL"),
|
||||||
|
(30, "SIGPWR"),
|
||||||
|
(31, "SIGSYS" | "SIGUNUSED")
|
||||||
|
);
|
||||||
|
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
signal_dict!(
|
||||||
|
(1, "SIGHUP"),
|
||||||
|
(2, "SIGINT"),
|
||||||
|
(3, "SIGQUIT"),
|
||||||
|
(4, "SIGILL"),
|
||||||
|
(5, "SIGTRAP"),
|
||||||
|
(6, "SIGABRT" | "SIGIOT"),
|
||||||
|
(7, "SIGEMT"),
|
||||||
|
(8, "SIGFPE"),
|
||||||
|
(9, "SIGKILL"),
|
||||||
|
(10, "SIGBUS"),
|
||||||
|
(11, "SIGSEGV"),
|
||||||
|
(12, "SIGSYS"),
|
||||||
|
(13, "SIGPIPE"),
|
||||||
|
(14, "SIGALRM"),
|
||||||
|
(15, "SIGTERM"),
|
||||||
|
(16, "SIGURG"),
|
||||||
|
(17, "SIGSTOP"),
|
||||||
|
(18, "SIGTSTP"),
|
||||||
|
(19, "SIGCONT"),
|
||||||
|
(20, "SIGCHLD"),
|
||||||
|
(21, "SIGTTIN"),
|
||||||
|
(22, "SIGTTOU"),
|
||||||
|
(23, "SIGIO"),
|
||||||
|
(24, "SIGXCPU"),
|
||||||
|
(25, "SIGXFSZ"),
|
||||||
|
(26, "SIGVTALRM"),
|
||||||
|
(27, "SIGPROF"),
|
||||||
|
(28, "SIGWINCH"),
|
||||||
|
(29, "SIGINFO"),
|
||||||
|
(30, "SIGUSR1"),
|
||||||
|
(31, "SIGUSR2")
|
||||||
|
);
|
||||||
|
|
||||||
|
#[cfg(any(target_os = "solaris", target_os = "illumos"))]
|
||||||
|
signal_dict!(
|
||||||
|
(1, "SIGHUP"),
|
||||||
|
(2, "SIGINT"),
|
||||||
|
(3, "SIGQUIT"),
|
||||||
|
(4, "SIGILL"),
|
||||||
|
(5, "SIGTRAP"),
|
||||||
|
(6, "SIGABRT" | "SIGIOT"),
|
||||||
|
(7, "SIGEMT"),
|
||||||
|
(8, "SIGFPE"),
|
||||||
|
(9, "SIGKILL"),
|
||||||
|
(10, "SIGBUS"),
|
||||||
|
(11, "SIGSEGV"),
|
||||||
|
(12, "SIGSYS"),
|
||||||
|
(13, "SIGPIPE"),
|
||||||
|
(14, "SIGALRM"),
|
||||||
|
(15, "SIGTERM"),
|
||||||
|
(16, "SIGUSR1"),
|
||||||
|
(17, "SIGUSR2"),
|
||||||
|
(18, "SIGCHLD"),
|
||||||
|
(19, "SIGPWR"),
|
||||||
|
(20, "SIGWINCH"),
|
||||||
|
(21, "SIGURG"),
|
||||||
|
(22, "SIGPOLL"),
|
||||||
|
(23, "SIGSTOP"),
|
||||||
|
(24, "SIGTSTP"),
|
||||||
|
(25, "SIGCONT"),
|
||||||
|
(26, "SIGTTIN"),
|
||||||
|
(27, "SIGTTOU"),
|
||||||
|
(28, "SIGVTALRM"),
|
||||||
|
(29, "SIGPROF"),
|
||||||
|
(30, "SIGXCPU"),
|
||||||
|
(31, "SIGXFSZ"),
|
||||||
|
(32, "SIGWAITING"),
|
||||||
|
(33, "SIGLWP"),
|
||||||
|
(34, "SIGFREEZE"),
|
||||||
|
(35, "SIGTHAW"),
|
||||||
|
(36, "SIGCANCEL"),
|
||||||
|
(37, "SIGLOST"),
|
||||||
|
(38, "SIGXRES"),
|
||||||
|
(39, "SIGJVM1"),
|
||||||
|
(40, "SIGJVM2")
|
||||||
|
);
|
||||||
|
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
signal_dict!((2, "SIGINT"), (21, "SIGBREAK"));
|
8
tests/specs/task/signals/__test__.jsonc
Normal file
8
tests/specs/task/signals/__test__.jsonc
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
// signals don't really exist on windows
|
||||||
|
"if": "unix",
|
||||||
|
// this runs a deno task
|
||||||
|
"args": "run -A --check sender.ts",
|
||||||
|
// just ensure this doesn't hang and completes successfully
|
||||||
|
"output": "[WILDCARD]"
|
||||||
|
}
|
5
tests/specs/task/signals/deno.jsonc
Normal file
5
tests/specs/task/signals/deno.jsonc
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"tasks": {
|
||||||
|
"listener": "deno run listener.ts"
|
||||||
|
}
|
||||||
|
}
|
16
tests/specs/task/signals/listener.ts
Normal file
16
tests/specs/task/signals/listener.ts
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
import { signals } from "./signals.ts";
|
||||||
|
|
||||||
|
for (const signal of signals) {
|
||||||
|
Deno.addSignalListener(signal, () => {
|
||||||
|
console.log("Received", signal);
|
||||||
|
if (signal === "SIGTERM") {
|
||||||
|
Deno.exit(0);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
setInterval(() => {
|
||||||
|
// keep alive
|
||||||
|
}, 1000);
|
||||||
|
|
||||||
|
console.log("Ready");
|
55
tests/specs/task/signals/sender.ts
Normal file
55
tests/specs/task/signals/sender.ts
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
import { signals } from "./signals.ts";
|
||||||
|
|
||||||
|
class StdoutReader {
|
||||||
|
readonly #reader: ReadableStreamDefaultReader<string>;
|
||||||
|
#text = "";
|
||||||
|
|
||||||
|
constructor(stream: ReadableStream<Uint8Array>) {
|
||||||
|
const textStream = stream.pipeThrough(new TextDecoderStream());
|
||||||
|
this.#reader = textStream.getReader();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Symbol.dispose]() {
|
||||||
|
this.#reader.releaseLock();
|
||||||
|
}
|
||||||
|
|
||||||
|
async waitForText(waitingText: string) {
|
||||||
|
if (this.#text.includes(waitingText)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
const { value, done } = await this.#reader.read();
|
||||||
|
if (value) {
|
||||||
|
this.#text += value;
|
||||||
|
if (this.#text.includes(waitingText)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (done) {
|
||||||
|
throw new Error("Did not find text: " + waitingText);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const command = new Deno.Command(Deno.execPath(), {
|
||||||
|
args: ["task", "listener"],
|
||||||
|
stdout: "piped",
|
||||||
|
});
|
||||||
|
|
||||||
|
const child = command.spawn();
|
||||||
|
const reader = new StdoutReader(child.stdout!);
|
||||||
|
await reader.waitForText("Ready");
|
||||||
|
|
||||||
|
for (const signal of signals) {
|
||||||
|
if (signal === "SIGTERM") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
console.error("Sending", signal);
|
||||||
|
child.kill(signal);
|
||||||
|
await reader.waitForText("Received " + signal);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.error("Sending SIGTERM");
|
||||||
|
child.kill("SIGTERM");
|
65
tests/specs/task/signals/signals.ts
Normal file
65
tests/specs/task/signals/signals.ts
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
const signals = [
|
||||||
|
"SIGABRT",
|
||||||
|
"SIGALRM",
|
||||||
|
"SIGBUS",
|
||||||
|
"SIGCHLD",
|
||||||
|
"SIGCONT",
|
||||||
|
"SIGEMT",
|
||||||
|
"SIGFPE",
|
||||||
|
"SIGHUP",
|
||||||
|
"SIGILL",
|
||||||
|
"SIGINFO",
|
||||||
|
"SIGINT",
|
||||||
|
"SIGIO",
|
||||||
|
"SIGPOLL",
|
||||||
|
"SIGPIPE",
|
||||||
|
"SIGPROF",
|
||||||
|
"SIGPWR",
|
||||||
|
"SIGQUIT",
|
||||||
|
"SIGSEGV",
|
||||||
|
"SIGSTKFLT",
|
||||||
|
"SIGSYS",
|
||||||
|
"SIGTERM",
|
||||||
|
"SIGTRAP",
|
||||||
|
"SIGTSTP",
|
||||||
|
"SIGTTIN",
|
||||||
|
"SIGTTOU",
|
||||||
|
"SIGURG",
|
||||||
|
"SIGUSR1",
|
||||||
|
"SIGUSR2",
|
||||||
|
"SIGVTALRM",
|
||||||
|
"SIGWINCH",
|
||||||
|
"SIGXCPU",
|
||||||
|
"SIGXFSZ",
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
// SIGKILL and SIGSTOP are not stoppable, SIGBREAK is for windows, and SIGUNUSED is not defined
|
||||||
|
type SignalsToTest = Exclude<
|
||||||
|
Deno.Signal,
|
||||||
|
"SIGKILL" | "SIGSTOP" | "SIGBREAK" | "SIGUNUSED"
|
||||||
|
>;
|
||||||
|
type EnsureAllSignalsIncluded = SignalsToTest extends typeof signals[number]
|
||||||
|
? typeof signals[number] extends SignalsToTest ? true
|
||||||
|
: never
|
||||||
|
: never;
|
||||||
|
const _checkSignals: EnsureAllSignalsIncluded = true;
|
||||||
|
|
||||||
|
const osSpecificSignals = signals.filter((s) => {
|
||||||
|
switch (s) {
|
||||||
|
case "SIGEMT":
|
||||||
|
return Deno.build.os === "darwin";
|
||||||
|
case "SIGINFO":
|
||||||
|
case "SIGFPE":
|
||||||
|
case "SIGILL":
|
||||||
|
case "SIGSEGV":
|
||||||
|
return Deno.build.os === "freebsd";
|
||||||
|
case "SIGPOLL":
|
||||||
|
case "SIGPWR":
|
||||||
|
case "SIGSTKFLT":
|
||||||
|
return Deno.build.os === "linux";
|
||||||
|
default:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export { osSpecificSignals as signals };
|
Loading…
Reference in a new issue