1use std::borrow::Cow;
4use std::cell::Cell;
5use std::cell::RefCell;
6use std::collections::HashMap;
7use std::ffi::OsStr;
8use std::ffi::OsString;
9use std::io::Read;
10use std::io::Write;
11use std::path::Path;
12use std::path::PathBuf;
13use std::rc::Rc;
14use std::rc::Weak;
15
16use anyhow::Result;
17use bitflags::bitflags;
18use futures::future::LocalBoxFuture;
19use tokio::sync::broadcast;
20use tokio::task::JoinHandle;
21
22use crate::shell::child_process_tracker::ChildProcessTracker;
23
24use super::commands::ShellCommand;
25use super::commands::builtin_commands;
26
27bitflags! {
28 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
30 pub struct ShellOptions: u32 {
31 const NULLGLOB = 1 << 0;
34 const FAILGLOB = 1 << 1;
37 const PIPEFAIL = 1 << 2;
40 const GLOBSTAR = 1 << 3;
43 }
44}
45
46impl Default for ShellOptions {
47 fn default() -> Self {
48 ShellOptions::GLOBSTAR
49 }
50}
51
52#[derive(Debug, Default, Clone)]
55pub(crate) struct TreeExitCodeCell(Rc<Cell<i32>>);
56
57impl TreeExitCodeCell {
58 pub fn try_set(&self, exit_code: i32) {
59 if self.0.get() == 0 {
60 self.0.set(exit_code);
62 }
63 }
64
65 pub fn get(&self) -> Option<i32> {
66 match self.0.get() {
67 0 => None,
68 code => Some(code),
69 }
70 }
71}
72
73#[derive(Clone)]
74pub struct ShellState {
75 env_vars: HashMap<OsString, OsString>,
78 shell_vars: HashMap<OsString, OsString>,
81 cwd: PathBuf,
82 commands: Rc<HashMap<String, Rc<dyn ShellCommand>>>,
83 kill_signal: KillSignal,
84 process_tracker: ChildProcessTracker,
85 tree_exit_code_cell: TreeExitCodeCell,
86 shell_options: ShellOptions,
88}
89
90impl ShellState {
91 pub fn new(
92 env_vars: HashMap<OsString, OsString>,
93 cwd: PathBuf,
94 custom_commands: HashMap<String, Rc<dyn ShellCommand>>,
95 kill_signal: KillSignal,
96 ) -> Self {
97 assert!(cwd.is_absolute());
98 let mut commands = builtin_commands();
99 commands.extend(custom_commands);
100 let mut result = Self {
101 env_vars: Default::default(),
102 shell_vars: Default::default(),
103 cwd: PathBuf::new(),
104 commands: Rc::new(commands),
105 kill_signal,
106 process_tracker: ChildProcessTracker::new(),
107 tree_exit_code_cell: Default::default(),
108 shell_options: ShellOptions::default(),
109 };
110 for (name, value) in env_vars {
112 result.apply_env_var(&name, &value);
113 }
114 result.set_cwd(cwd);
115 result
116 }
117
118 pub fn cwd(&self) -> &PathBuf {
119 &self.cwd
120 }
121
122 pub fn env_vars(&self) -> &HashMap<OsString, OsString> {
123 &self.env_vars
124 }
125
126 pub fn get_var(&self, name: &OsStr) -> Option<&OsString> {
127 let name = if cfg!(windows) {
128 Cow::Owned(name.to_ascii_uppercase())
129 } else {
130 Cow::Borrowed(name)
131 };
132 let name: &OsStr = &name;
133 self
134 .env_vars
135 .get(name)
136 .or_else(|| self.shell_vars.get(name))
137 }
138
139 pub fn set_cwd(&mut self, cwd: PathBuf) {
140 self.cwd = cwd.clone();
141 self.env_vars.insert("PWD".into(), cwd.into_os_string());
143 }
144
145 pub fn apply_changes(&mut self, changes: &[EnvChange]) {
146 for change in changes {
147 self.apply_change(change);
148 }
149 }
150
151 pub fn apply_change(&mut self, change: &EnvChange) {
152 match change {
153 EnvChange::SetEnvVar(name, value) => self.apply_env_var(name, value),
154 EnvChange::SetShellVar(name, value) => {
155 if self.env_vars.contains_key(name) {
156 self.apply_env_var(name, value);
157 } else {
158 self
159 .shell_vars
160 .insert(name.to_os_string(), value.to_os_string());
161 }
162 }
163 EnvChange::UnsetVar(name) => {
164 self.shell_vars.remove(name);
165 self.env_vars.remove(name);
166 }
167 EnvChange::Cd(new_dir) => {
168 self.set_cwd(new_dir.clone());
169 }
170 EnvChange::SetOption(option, enabled) => {
171 self.set_shell_option(*option, *enabled);
172 }
173 }
174 }
175
176 pub fn apply_env_var(&mut self, name: &OsStr, value: &OsStr) {
177 let name = if cfg!(windows) {
178 name.to_ascii_uppercase()
180 } else {
181 name.to_os_string()
182 };
183 if name == "PWD" {
184 let cwd = Path::new(value);
185 if cwd.is_absolute()
186 && let Ok(cwd) = deno_path_util::fs::canonicalize_path_maybe_not_exists(
187 &sys_traits::impls::RealSys,
188 cwd,
189 )
190 {
191 self.set_cwd(cwd);
193 }
194 } else {
195 self.shell_vars.remove(&name);
196 self.env_vars.insert(name, value.to_os_string());
197 }
198 }
199
200 pub fn kill_signal(&self) -> &KillSignal {
201 &self.kill_signal
202 }
203
204 pub fn shell_options(&self) -> ShellOptions {
205 self.shell_options
206 }
207
208 pub fn set_shell_option(&mut self, option: ShellOptions, enabled: bool) {
209 if enabled {
210 self.shell_options.insert(option);
211 } else {
212 self.shell_options.remove(option);
213 }
214 }
215
216 pub fn track_child_process(&self, child: &tokio::process::Child) {
217 self.process_tracker.track(child);
218 }
219
220 pub(crate) fn tree_exit_code_cell(&self) -> &TreeExitCodeCell {
221 &self.tree_exit_code_cell
222 }
223
224 pub fn resolve_custom_command(
226 &self,
227 name: &OsStr,
228 ) -> Option<Rc<dyn ShellCommand>> {
229 name
231 .to_str()
232 .and_then(|name| self.commands.get(name).cloned())
234 }
235
236 pub fn resolve_command_path(
240 &self,
241 command_name: &OsStr,
242 ) -> Result<PathBuf, super::which::CommandPathResolutionError> {
243 super::which::resolve_command_path(command_name, self.cwd(), self)
244 }
245
246 pub fn with_child_signal(&self) -> ShellState {
247 let mut state = self.clone();
248 state.kill_signal = self.kill_signal.child_signal();
249 state.tree_exit_code_cell = TreeExitCodeCell::default();
250 state
251 }
252}
253
254impl sys_traits::BaseEnvVar for ShellState {
255 fn base_env_var_os(&self, key: &OsStr) -> Option<OsString> {
256 self.env_vars.get(key).cloned()
257 }
258}
259
260#[derive(Debug, Clone, PartialEq, Eq)]
261pub enum EnvChange {
262 SetEnvVar(OsString, OsString),
264 SetShellVar(OsString, OsString),
266 UnsetVar(OsString),
268 Cd(PathBuf),
269 SetOption(ShellOptions, bool),
271}
272
273pub type FutureExecuteResult = LocalBoxFuture<'static, ExecuteResult>;
274
275#[derive(Debug)]
276pub enum ExecuteResult {
277 Exit(i32, Vec<JoinHandle<i32>>),
278 Continue(i32, Vec<EnvChange>, Vec<JoinHandle<i32>>),
279}
280
281impl ExecuteResult {
282 pub fn from_exit_code(exit_code: i32) -> ExecuteResult {
283 ExecuteResult::Continue(exit_code, Vec::new(), Vec::new())
284 }
285
286 pub fn into_exit_code_and_handles(self) -> (i32, Vec<JoinHandle<i32>>) {
287 match self {
288 ExecuteResult::Exit(code, handles) => (code, handles),
289 ExecuteResult::Continue(code, _, handles) => (code, handles),
290 }
291 }
292
293 pub fn into_handles(self) -> Vec<JoinHandle<i32>> {
294 self.into_exit_code_and_handles().1
295 }
296}
297
298#[derive(Debug)]
300pub enum ShellPipeReader {
301 OsPipe(std::io::PipeReader),
302 StdFile(std::fs::File),
303}
304
305impl Clone for ShellPipeReader {
306 fn clone(&self) -> Self {
307 match self {
308 Self::OsPipe(pipe) => Self::OsPipe(pipe.try_clone().unwrap()),
309 Self::StdFile(file) => Self::StdFile(file.try_clone().unwrap()),
310 }
311 }
312}
313
314impl ShellPipeReader {
315 pub fn stdin() -> ShellPipeReader {
316 #[cfg(unix)]
317 pub fn dup_stdin_as_pipe_reader() -> std::io::PipeReader {
318 use std::os::fd::AsFd;
319 use std::os::fd::FromRawFd;
320 use std::os::fd::IntoRawFd;
321 let owned = std::io::stdin().as_fd().try_clone_to_owned().unwrap();
322 let raw = owned.into_raw_fd();
323 unsafe { std::io::PipeReader::from_raw_fd(raw) }
325 }
326
327 #[cfg(windows)]
328 pub fn dup_stdin_as_pipe_reader() -> std::io::PipeReader {
329 use std::os::windows::io::AsHandle;
330 use std::os::windows::io::FromRawHandle;
331 use std::os::windows::io::IntoRawHandle;
332 let owned = std::io::stdin().as_handle().try_clone_to_owned().unwrap();
333 let raw = owned.into_raw_handle();
334 unsafe { std::io::PipeReader::from_raw_handle(raw) }
336 }
337
338 ShellPipeReader::OsPipe(dup_stdin_as_pipe_reader())
339 }
340
341 pub fn from_raw(reader: std::io::PipeReader) -> Self {
342 Self::OsPipe(reader)
343 }
344
345 pub fn from_std(std_file: std::fs::File) -> Self {
346 Self::StdFile(std_file)
347 }
348
349 #[cfg(test)]
350 #[allow(clippy::should_implement_trait)]
351 pub fn from_str(data: &str) -> Self {
352 use std::io::Write;
353 let (read, mut write) = std::io::pipe().unwrap();
354 write.write_all(data.as_bytes()).unwrap();
355 Self::OsPipe(read)
356 }
357
358 pub fn into_stdio(self) -> std::process::Stdio {
359 match self {
360 Self::OsPipe(pipe) => pipe.into(),
361 Self::StdFile(file) => file.into(),
362 }
363 }
364
365 pub fn pipe_to(self, writer: &mut dyn Write) -> Result<()> {
367 self.pipe_to_inner(writer, false)
370 }
371
372 fn pipe_to_with_flushing(self, writer: &mut dyn Write) -> Result<()> {
373 self.pipe_to_inner(writer, true)
374 }
375
376 fn pipe_to_inner(
377 mut self,
378 writer: &mut dyn Write,
379 flush: bool,
380 ) -> Result<()> {
381 loop {
382 let mut buffer = [0; 512]; let size = match &mut self {
384 ShellPipeReader::OsPipe(pipe) => pipe.read(&mut buffer)?,
385 ShellPipeReader::StdFile(file) => file.read(&mut buffer)?,
386 };
387 if size == 0 {
388 break;
389 }
390 writer.write_all(&buffer[0..size])?;
391 if flush {
392 writer.flush()?;
393 }
394 }
395 Ok(())
396 }
397
398 pub fn pipe_to_sender(self, mut sender: ShellPipeWriter) -> Result<()> {
400 match &mut sender {
401 ShellPipeWriter::OsPipe(pipe) => self.pipe_to(pipe),
402 ShellPipeWriter::StdFile(file) => self.pipe_to(file),
403 ShellPipeWriter::Stdout => {
408 self.pipe_to_with_flushing(&mut std::io::stdout())
409 }
410 ShellPipeWriter::Stderr => {
411 self.pipe_to_with_flushing(&mut std::io::stderr())
412 }
413 ShellPipeWriter::Null => Ok(()),
414 }
415 }
416
417 pub fn pipe_to_string_handle(self) -> JoinHandle<String> {
420 tokio::task::spawn_blocking(|| {
421 let mut buf = Vec::new();
422 self.pipe_to(&mut buf).unwrap();
423 String::from_utf8_lossy(&buf).to_string()
424 })
425 }
426
427 pub fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
428 match self {
429 ShellPipeReader::OsPipe(pipe) => pipe.read(buf).map_err(|e| e.into()),
430 ShellPipeReader::StdFile(file) => file.read(buf).map_err(|e| e.into()),
431 }
432 }
433}
434
435#[derive(Debug)]
440pub enum ShellPipeWriter {
441 OsPipe(std::io::PipeWriter),
442 StdFile(std::fs::File),
443 Stdout,
449 Stderr,
450 Null,
451}
452
453impl Clone for ShellPipeWriter {
454 fn clone(&self) -> Self {
455 match self {
456 Self::OsPipe(pipe) => Self::OsPipe(pipe.try_clone().unwrap()),
457 Self::StdFile(file) => Self::StdFile(file.try_clone().unwrap()),
458 Self::Stdout => Self::Stdout,
459 Self::Stderr => Self::Stderr,
460 Self::Null => Self::Null,
461 }
462 }
463}
464
465impl ShellPipeWriter {
466 pub fn stdout() -> Self {
467 Self::Stdout
468 }
469
470 pub fn stderr() -> Self {
471 Self::Stderr
472 }
473
474 pub fn null() -> Self {
475 Self::Null
476 }
477
478 pub fn from_std(std_file: std::fs::File) -> Self {
479 Self::StdFile(std_file)
480 }
481
482 pub fn into_stdio(self) -> std::process::Stdio {
483 match self {
484 Self::OsPipe(pipe) => pipe.into(),
485 Self::StdFile(file) => file.into(),
486 Self::Stdout => std::process::Stdio::inherit(),
487 Self::Stderr => std::process::Stdio::inherit(),
488 Self::Null => std::process::Stdio::null(),
489 }
490 }
491
492 pub fn write_all(&mut self, bytes: &[u8]) -> Result<()> {
493 self.write_all_iter(std::iter::once(bytes))
494 }
495
496 pub fn write_all_iter<'a>(
497 &mut self,
498 iter: impl Iterator<Item = &'a [u8]> + 'a,
499 ) -> Result<()> {
500 match self {
501 Self::OsPipe(pipe) => {
502 for bytes in iter {
503 pipe.write_all(bytes)?;
504 }
505 }
506 Self::StdFile(file) => {
507 for bytes in iter {
508 file.write_all(bytes)?
509 }
510 }
511 Self::Stdout => {
514 let mut stdout = std::io::stdout().lock();
515 for bytes in iter {
516 stdout.write_all(bytes)?;
517 }
518 stdout.flush()?;
519 }
520 Self::Stderr => {
521 let mut stderr = std::io::stderr().lock();
522 for bytes in iter {
523 stderr.write_all(bytes)?;
524 }
525 stderr.flush()?;
526 }
527 Self::Null => {}
528 }
529 Ok(())
530 }
531
532 pub fn write_line(&mut self, line: &str) -> Result<()> {
533 let bytes = format!("{line}\n");
534 self.write_all(bytes.as_bytes())
535 }
536}
537
538pub fn pipe() -> (ShellPipeReader, ShellPipeWriter) {
540 let (reader, writer) = std::io::pipe().unwrap();
541 (
542 ShellPipeReader::OsPipe(reader),
543 ShellPipeWriter::OsPipe(writer),
544 )
545}
546
547#[derive(Debug)]
548struct KillSignalInner {
549 aborted_code: RefCell<Option<i32>>,
556 sender: broadcast::Sender<SignalKind>,
557 children: RefCell<Vec<Weak<KillSignalInner>>>,
558}
559
560impl KillSignalInner {
561 pub fn send(&self, signal_kind: SignalKind) {
562 if signal_kind.causes_abort() {
563 let mut stored_aborted_code = self.aborted_code.borrow_mut();
564 if stored_aborted_code.is_none() {
565 *stored_aborted_code = Some(signal_kind.aborted_code());
566 }
567 }
568 _ = self.sender.send(signal_kind);
569
570 self.children.borrow_mut().retain(|weak_child| {
572 if let Some(child) = weak_child.upgrade() {
573 child.send(signal_kind);
574 true
575 } else {
576 false }
578 });
579 }
580}
581
582#[derive(Debug, Clone)]
584pub struct KillSignal(Rc<KillSignalInner>);
585
586impl Default for KillSignal {
587 fn default() -> Self {
588 let (sender, _) = broadcast::channel(100);
589 Self(Rc::new(KillSignalInner {
590 aborted_code: RefCell::new(None),
591 sender,
592 children: Default::default(),
593 }))
594 }
595}
596
597impl KillSignal {
598 pub fn aborted_code(&self) -> Option<i32> {
600 *self.0.aborted_code.borrow()
601 }
602
603 pub fn child_signal(&self) -> Self {
606 let (sender, _) = broadcast::channel(100);
607 let child = Rc::new(KillSignalInner {
608 aborted_code: RefCell::new(self.aborted_code()),
609 sender,
610 children: RefCell::new(Vec::new()),
611 });
612
613 self.0.children.borrow_mut().push(Rc::downgrade(&child));
615
616 Self(child)
617 }
618
619 pub fn drop_guard(self) -> KillSignalDropGuard {
621 self.drop_guard_with_kind(SignalKind::SIGTERM)
622 }
623
624 pub fn drop_guard_with_kind(self, kind: SignalKind) -> KillSignalDropGuard {
626 KillSignalDropGuard {
627 disarmed: Cell::new(false),
628 kill_signal_kind: kind,
629 signal: self,
630 }
631 }
632
633 pub fn send(&self, signal: SignalKind) {
635 self.0.send(signal)
636 }
637
638 pub async fn wait_aborted(&self) -> SignalKind {
640 let mut receiver = self.0.sender.subscribe();
641 loop {
642 let signal = receiver.recv().await.unwrap();
644 if signal.causes_abort() {
645 return signal;
646 }
647 }
648 }
649
650 pub async fn wait_any(&self) -> SignalKind {
652 let mut receiver = self.0.sender.subscribe();
653 receiver.recv().await.unwrap()
655 }
656}
657
658#[derive(Debug)]
660pub struct KillSignalDropGuard {
661 disarmed: Cell<bool>,
662 kill_signal_kind: SignalKind,
663 signal: KillSignal,
664}
665
666impl Drop for KillSignalDropGuard {
667 fn drop(&mut self) {
668 if !self.disarmed.get() {
669 self.signal.send(self.kill_signal_kind);
670 }
671 }
672}
673
674impl KillSignalDropGuard {
675 pub fn disarm(&self) {
677 self.disarmed.set(true);
678 }
679}
680
681#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
682pub enum SignalKind {
683 SIGTERM,
684 SIGKILL,
685 SIGABRT,
686 SIGQUIT,
687 SIGINT,
688 SIGSTOP,
689 Other(i32),
690}
691
692impl SignalKind {
693 pub fn causes_abort(&self) -> bool {
694 match self {
695 SignalKind::SIGTERM
696 | SignalKind::SIGKILL
697 | SignalKind::SIGQUIT
698 | SignalKind::SIGINT
699 | SignalKind::SIGSTOP
700 | SignalKind::SIGABRT => true,
702 SignalKind::Other(_) => false,
703 }
704 }
705
706 pub fn aborted_code(&self) -> i32 {
707 let value: i32 = (*self).into();
708 128 + value
709 }
710}
711
712impl From<i32> for SignalKind {
713 fn from(value: i32) -> Self {
714 #[cfg(unix)]
715 match value {
716 nix::libc::SIGINT => SignalKind::SIGINT,
717 nix::libc::SIGQUIT => SignalKind::SIGQUIT,
718 nix::libc::SIGABRT => SignalKind::SIGABRT,
719 nix::libc::SIGKILL => SignalKind::SIGKILL,
720 nix::libc::SIGTERM => SignalKind::SIGTERM,
721 nix::libc::SIGSTOP => SignalKind::SIGSTOP,
722 _ => SignalKind::Other(value),
723 }
724 #[cfg(not(unix))]
725 match value {
726 2 => SignalKind::SIGINT,
727 3 => SignalKind::SIGQUIT,
728 6 => SignalKind::SIGABRT,
729 9 => SignalKind::SIGKILL,
730 15 => SignalKind::SIGTERM,
731 19 => SignalKind::SIGSTOP,
732 _ => SignalKind::Other(value),
733 }
734 }
735}
736
737impl From<SignalKind> for i32 {
738 fn from(kind: SignalKind) -> i32 {
739 #[cfg(unix)]
740 match kind {
741 SignalKind::SIGINT => nix::libc::SIGINT,
742 SignalKind::SIGQUIT => nix::libc::SIGQUIT,
743 SignalKind::SIGABRT => nix::libc::SIGABRT,
744 SignalKind::SIGKILL => nix::libc::SIGKILL,
745 SignalKind::SIGTERM => nix::libc::SIGTERM,
746 SignalKind::SIGSTOP => nix::libc::SIGSTOP,
747 SignalKind::Other(value) => value,
748 }
749 #[cfg(not(unix))]
750 match kind {
751 SignalKind::SIGINT => 2,
752 SignalKind::SIGQUIT => 3,
753 SignalKind::SIGABRT => 6,
754 SignalKind::SIGKILL => 9,
755 SignalKind::SIGTERM => 15,
756 SignalKind::SIGSTOP => 19,
757 SignalKind::Other(value) => value,
758 }
759 }
760}
761
762#[cfg(test)]
763mod test {
764 use crate::KillSignal;
765 use crate::SignalKind;
766
767 #[tokio::test]
768 async fn test_send_and_wait_any() {
769 let kill_signal = KillSignal::default();
770
771 let signal_sender = kill_signal.clone();
773 deno_unsync::spawn(async move {
774 signal_sender.send(SignalKind::SIGTERM);
775 });
776
777 let signal = kill_signal.wait_any().await;
779 assert_eq!(signal, SignalKind::SIGTERM);
780 }
781
782 #[tokio::test]
783 async fn test_signal_propagation_to_child_and_grandchild() {
784 let parent_signal = KillSignal::default();
785 let child_signal = parent_signal.child_signal();
786 let sibling_signal = parent_signal.child_signal();
787 let grandchild_signal = child_signal.child_signal();
788
789 let parent = parent_signal.clone();
791 deno_unsync::spawn(async move {
792 parent.send(SignalKind::SIGKILL);
793 });
794
795 let signals = futures::join!(
796 child_signal.wait_any(),
797 sibling_signal.wait_any(),
798 grandchild_signal.wait_any()
799 );
800
801 for signal in [signals.0, signals.1, signals.2].into_iter() {
802 assert_eq!(signal, SignalKind::SIGKILL);
803 }
804 assert_eq!(child_signal.aborted_code(), Some(128 + 9));
805 assert_eq!(sibling_signal.aborted_code(), Some(128 + 9));
806 assert_eq!(grandchild_signal.aborted_code(), Some(128 + 9));
807 }
808
809 #[tokio::test]
810 async fn test_signal_propagation_on_sub_tree() {
811 let parent_signal = KillSignal::default();
812 let child_signal = parent_signal.child_signal();
813 let sibling_signal = parent_signal.child_signal();
814 let grandchild_signal = child_signal.child_signal();
815 let grandchild2_signal = child_signal.child_signal();
816
817 child_signal.send(SignalKind::SIGABRT);
818
819 assert!(parent_signal.aborted_code().is_none());
820 assert!(sibling_signal.aborted_code().is_none());
821 assert!(child_signal.aborted_code().is_some());
822 assert!(grandchild_signal.aborted_code().is_some());
823 assert!(grandchild2_signal.aborted_code().is_some());
824 }
825
826 #[tokio::test]
827 async fn test_wait_aborted() {
828 let kill_signal = KillSignal::default();
829
830 let signal_sender = kill_signal.clone();
832 deno_unsync::spawn(async move {
833 signal_sender.send(SignalKind::SIGABRT);
834 });
835
836 let signal = kill_signal.wait_aborted().await;
838 assert_eq!(signal, SignalKind::SIGABRT);
839 assert!(kill_signal.aborted_code().is_some());
840 }
841
842 #[tokio::test]
843 async fn test_propagation_and_is_aborted_flag() {
844 let parent_signal = KillSignal::default();
845 let child_signal = parent_signal.child_signal();
846
847 assert!(parent_signal.aborted_code().is_none());
848 assert!(child_signal.aborted_code().is_none());
849
850 deno_unsync::spawn({
852 let parent_signal = parent_signal.clone();
853 async move {
854 parent_signal.send(SignalKind::SIGQUIT);
855 }
856 });
857
858 let signal = child_signal.wait_aborted().await;
860 assert_eq!(signal, SignalKind::SIGQUIT);
861 assert_eq!(parent_signal.aborted_code(), Some(128 + 3));
862 assert_eq!(child_signal.aborted_code(), Some(128 + 3));
863 }
864
865 #[tokio::test]
866 async fn test_dropped_child_signal_cleanup() {
867 let parent_signal = KillSignal::default();
868
869 {
871 let child_signal = parent_signal.child_signal();
872 assert!(child_signal.aborted_code().is_none());
873 }
874
875 deno_unsync::spawn({
877 let parent_signal = parent_signal.clone();
878 async move {
879 parent_signal.send(SignalKind::SIGTERM);
880 }
881 });
882
883 let signal = parent_signal.wait_any().await;
885 assert_eq!(signal, SignalKind::SIGTERM);
886 }
887
888 #[tokio::test]
889 async fn test_drop_guard() {
890 let parent_signal = KillSignal::default();
891
892 {
894 let drop_guard = parent_signal.clone().drop_guard();
895 drop_guard.disarm();
896 }
897 assert_eq!(parent_signal.aborted_code(), None);
898
899 {
901 let drop_guard = parent_signal.clone().drop_guard();
902 drop(drop_guard);
903 }
904 assert_eq!(
905 parent_signal.aborted_code(),
906 Some(SignalKind::SIGTERM.aborted_code())
907 );
908 }
909}