summaryrefslogtreecommitdiff
path: root/runtime/ops/signal.rs
diff options
context:
space:
mode:
authorNathan Whitaker <17734409+nathanwhit@users.noreply.github.com>2024-03-11 10:22:28 -0700
committerGitHub <noreply@github.com>2024-03-11 10:22:28 -0700
commit28b362adfc49324e20af5ecb1530f89eb91c4ed5 (patch)
treec0e5cc5b08d13a2a10f2f72281996090356af61c /runtime/ops/signal.rs
parent670e9a9be78e15c2800541aeba62d7cba6a4d58d (diff)
fix(runtime): Restore default signal handler after user handlers are unregistered (#22757)
<!-- Before submitting a PR, please read https://docs.deno.com/runtime/manual/references/contributing 1. Give the PR a descriptive title. Examples of good title: - fix(std/http): Fix race condition in server - docs(console): Update docstrings - feat(doc): Handle nested reexports Examples of bad title: - fix #7123 - update docs - fix bugs 2. Ensure there is a related issue and it is referenced in the PR text. 3. Ensure there are tests that cover the changes. 4. Ensure `cargo test` passes. 5. Ensure `./tools/format.js` passes without changing files. 6. Ensure `./tools/lint.js` passes. 7. Open as a draft PR if your work is still in progress. The CI won't run all steps, but you can add '[ci]' to a commit message to force it to. 8. If you would like to run the benchmarks on the CI, add the 'ci-bench' label. --> Fixes #22724. Fixes #7164. This does add a dependency on `signal-hook`, but it's just a higher level API on top of `signal-hook-registry` (which we and `tokio` already depend on) and doesn't add any transitive deps.
Diffstat (limited to 'runtime/ops/signal.rs')
-rw-r--r--runtime/ops/signal.rs76
1 files changed, 74 insertions, 2 deletions
diff --git a/runtime/ops/signal.rs b/runtime/ops/signal.rs
index 01a07e52a..5aaf1ddb2 100644
--- a/runtime/ops/signal.rs
+++ b/runtime/ops/signal.rs
@@ -12,7 +12,13 @@ use deno_core::ResourceId;
use std::borrow::Cow;
use std::cell::RefCell;
+#[cfg(unix)]
+use std::collections::BTreeMap;
use std::rc::Rc;
+#[cfg(unix)]
+use std::sync::atomic::AtomicBool;
+#[cfg(unix)]
+use std::sync::Arc;
#[cfg(unix)]
use tokio::signal::unix::signal;
@@ -32,13 +38,52 @@ use tokio::signal::windows::CtrlC;
deno_core::extension!(
deno_signal,
ops = [op_signal_bind, op_signal_unbind, op_signal_poll],
+ state = |state| {
+ #[cfg(unix)]
+ {
+ state.put(SignalState::default());
+ }
+ }
);
#[cfg(unix)]
+#[derive(Default)]
+struct SignalState {
+ enable_default_handlers: BTreeMap<libc::c_int, Arc<AtomicBool>>,
+}
+
+#[cfg(unix)]
+impl SignalState {
+ /// Disable the default signal handler for the given signal.
+ ///
+ /// Returns the shared flag to enable the default handler later, and whether a default handler already existed.
+ fn disable_default_handler(
+ &mut self,
+ signo: libc::c_int,
+ ) -> (Arc<AtomicBool>, bool) {
+ use std::collections::btree_map::Entry;
+
+ match self.enable_default_handlers.entry(signo) {
+ Entry::Occupied(entry) => {
+ let enable = entry.get();
+ enable.store(false, std::sync::atomic::Ordering::Release);
+ (enable.clone(), true)
+ }
+ Entry::Vacant(entry) => {
+ let enable = Arc::new(AtomicBool::new(false));
+ entry.insert(enable.clone());
+ (enable, false)
+ }
+ }
+ }
+}
+
+#[cfg(unix)]
/// The resource for signal stream.
/// The second element is the waker of polling future.
struct SignalStreamResource {
signal: AsyncRefCell<Signal>,
+ enable_default_handler: Arc<AtomicBool>,
cancel: CancelHandle,
}
@@ -548,11 +593,29 @@ fn op_signal_bind(
"Binding to signal '{sig}' is not allowed",
)));
}
+
+ let signal = AsyncRefCell::new(signal(SignalKind::from_raw(signo))?);
+
+ let (enable_default_handler, has_default_handler) = state
+ .borrow_mut::<SignalState>()
+ .disable_default_handler(signo);
+
let resource = SignalStreamResource {
- signal: AsyncRefCell::new(signal(SignalKind::from_raw(signo))?),
+ signal,
cancel: Default::default(),
+ enable_default_handler: enable_default_handler.clone(),
};
let rid = state.resource_table.add(resource);
+
+ if !has_default_handler {
+ // restore default signal handler when the signal is unbound
+ // this can error if the signal is not supported, if so let's just leave it as is
+ let _ = signal_hook::flag::register_conditional_default(
+ signo,
+ enable_default_handler,
+ );
+ }
+
Ok(rid)
}
@@ -606,6 +669,15 @@ pub fn op_signal_unbind(
state: &mut OpState,
#[smi] rid: ResourceId,
) -> Result<(), AnyError> {
- state.resource_table.take_any(rid)?.close();
+ let resource = state.resource_table.take::<SignalStreamResource>(rid)?;
+
+ #[cfg(unix)]
+ {
+ resource
+ .enable_default_handler
+ .store(true, std::sync::atomic::Ordering::Release);
+ }
+
+ resource.close();
Ok(())
}