diff options
author | Nathan Whitaker <17734409+nathanwhit@users.noreply.github.com> | 2024-03-11 10:22:28 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-03-11 10:22:28 -0700 |
commit | 28b362adfc49324e20af5ecb1530f89eb91c4ed5 (patch) | |
tree | c0e5cc5b08d13a2a10f2f72281996090356af61c /runtime/ops/signal.rs | |
parent | 670e9a9be78e15c2800541aeba62d7cba6a4d58d (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.rs | 76 |
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(()) } |