Repository: denoland/deno
Pull request: feat(signals): support listening for ctrl+close on Windows

Waiting on:

* https://github.com/tokio-rs/tokio/issues/7039

part of https://github.com/denoland/deno/issues/26331

Closes https://github.com/denoland/deno/issues/16298

Changed files:
filename: ext/os/ops/signal.rs
patch: @@ -25,13 +25,11 @@ use tokio::signal::unix::Signal;
 #[cfg(unix)]
 use tokio::signal::unix::SignalKind;
 #[cfg(windows)]
-use tokio::signal::windows::ctrl_break;
-#[cfg(windows)]
-use tokio::signal::windows::ctrl_c;
-#[cfg(windows)]
 use tokio::signal::windows::CtrlBreak;
 #[cfg(windows)]
 use tokio::signal::windows::CtrlC;
+#[cfg(windows)]
+use tokio::signal::windows::CtrlClose;
 
 #[derive(Debug, thiserror::Error, deno_error::JsError)]
 pub enum SignalError {
@@ -107,28 +105,37 @@ impl Resource for SignalStreamResource {
 enum WindowsSignal {
   Sigint(CtrlC),
   Sigbreak(CtrlBreak),
+  Sighup(CtrlClose),
 }
 
 #[cfg(windows)]
 impl From<CtrlC> for WindowsSignal {
   fn from(ctrl_c: CtrlC) -> Self {
-    WindowsSignal::Sigint(ctrl_c)
+    Self::Sigint(ctrl_c)
   }
 }
 
 #[cfg(windows)]
 impl From<CtrlBreak> for WindowsSignal {
   fn from(ctrl_break: CtrlBreak) -> Self {
-    WindowsSignal::Sigbreak(ctrl_break)
+    Self::Sigbreak(ctrl_break)
+  }
+}
+
+#[cfg(windows)]
+impl From<CtrlClose> for WindowsSignal {
+  fn from(ctrl_close: CtrlClose) -> Self {
+    Self::Sighup(ctrl_close)
   }
 }
 
 #[cfg(windows)]
 impl WindowsSignal {
   pub async fn recv(&mut self) -> Option<()> {
     match self {
-      WindowsSignal::Sigint(ctrl_c) => ctrl_c.recv().await,
-      WindowsSignal::Sigbreak(ctrl_break) => ctrl_break.recv().await,
+      Self::Sigint(ctrl_c) => ctrl_c.recv().await,
+      Self::Sigbreak(ctrl_break) => ctrl_break.recv().await,
+      Self::Sighup(ctrl_close) => ctrl_close.recv().await,
     }
   }
 }
@@ -194,18 +201,20 @@ pub fn op_signal_bind(
   state: &mut OpState,
   #[string] sig: &str,
 ) -> Result<ResourceId, SignalError> {
-  let signo = crate::signal::signal_str_to_int(sig)?;
+  use tokio::signal::windows::ctrl_break;
+  use tokio::signal::windows::ctrl_c;
+  use tokio::signal::windows::ctrl_close;
+
   let resource = SignalStreamResource {
-    signal: AsyncRefCell::new(match signo {
-      // SIGINT
-      2 => ctrl_c()
-        .expect("There was an issue creating ctrl+c event stream.")
-        .into(),
-      // SIGBREAK
-      21 => ctrl_break()
-        .expect("There was an issue creating ctrl+break event stream.")
-        .into(),
-      _ => unimplemented!(),
+    signal: AsyncRefCell::new(match sig {
+      "SIGHUP" => ctrl_close()?.into(),
+      "SIGINT" => ctrl_c()?.into(),
+      "SIGBREAK" => ctrl_break()?.into(),
+      _ => {
+        return Err(
+          crate::signal::InvalidSignalStrError(sig.to_string()).into(),
+        );
+      }
     }),
     cancel: Default::default(),
   };

filename: ext/os/signal.rs
patch: @@ -2,7 +2,7 @@
 
 #[cfg(target_os = "windows")]
 #[derive(Debug, thiserror::Error)]
-#[error("Windows only supports ctrl-c (SIGINT) and ctrl-break (SIGBREAK), but got {0}")]
+#[error("Windows only supports ctrl-c (SIGINT), ctrl-break (SIGBREAK), and ctrl-close (SIGUP), but got {0}")]
 pub struct InvalidSignalStrError(pub String);
 
 #[cfg(any(
@@ -20,7 +20,7 @@ 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}")]
+#[error("Windows only supports ctrl-c (SIGINT), ctrl-break (SIGBREAK), and ctrl-close (SIGUP), but got {0}")]
 pub struct InvalidSignalIntError(pub libc::c_int);
 
 #[cfg(any(
@@ -254,4 +254,4 @@ signal_dict!(
 );
 
 #[cfg(target_os = "windows")]
-signal_dict!((2, "SIGINT"), (21, "SIGBREAK"));
+signal_dict!((1, "SIGHUP"), (2, "SIGINT"), (21, "SIGBREAK"));

filename: tests/unit/signal_test.ts
patch: @@ -5,7 +5,7 @@ Deno.test(
   { ignore: Deno.build.os !== "windows" },
   function signalsNotImplemented() {
     const msg =
-      "Windows only supports ctrl-c (SIGINT) and ctrl-break (SIGBREAK), but got ";
+      "Windows only supports ctrl-c (SIGINT), ctrl-break (SIGBREAK), and ctrl-close (SIGUP), but got ";
     assertThrows(
       () => {
         Deno.addSignalListener("SIGALRM", () => {});
@@ -20,13 +20,6 @@ Deno.test(
       Error,
       msg + "SIGCHLD",
     );
-    assertThrows(
-      () => {
-        Deno.addSignalListener("SIGHUP", () => {});
-      },
-      Error,
-      msg + "SIGHUP",
-    );
     assertThrows(
       () => {
         Deno.addSignalListener("SIGIO", () => {});