Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

It should be possible to handle SIGINT that is raised inside a looped prompt #27465

Closed
finleyjb opened this issue Dec 24, 2024 · 3 comments
Closed
Labels
invalid what appeared to be an issue with Deno wasn't

Comments

@finleyjb
Copy link

finleyjb commented Dec 24, 2024

Version: Deno 2.0.0

Given the following code:

// Example 1
Deno.addSignalListener("SIGINT", () => {
  console.log("interrupted!"); // Never prints
  Deno.exit();
});

while (true) {
  const val = prompt('Prompt: '); // Hit Ctrl-c while in this prompt
  console.log(val); // logs null
}

As it stands right now, this program will log a null and the SIGINT handler will never run. However, if I remove the loop and run this the SIGINT handler runs (adapted from the deno docs):

// Example 2
let sigint = false;
Deno.addSignalListener("SIGINT", () => {
  console.log("interrupted!");
  sigint = true;
});

const val = prompt("Prompt: ");
console.log(val); // prints null

// Add a timeout to prevent process exiting immediately.
setTimeout(() => {}, 5000); // prints "interrupted!"

The only workaround I've found is that you can yield control using a setTimeout in a promise:

// Example 3
Deno.addSignalListener("SIGINT", () => {
  console.log("interrupted!");
  Deno.exit(1);
});

while (true) {
  const val = prompt("Prompt: ");
  const printHandlerPromise = new Promise((resolve, reject) => {
    console.log(val);
    setTimeout(() => {
      resolve(null);
    }, 0);
  });
  await printHandlerPromise;
}

It seems like Example 1 should do something other than silently swallow the SIGINT function call. Perhaps the handler should run after the user inputs text at the next prompt?

@finleyjb finleyjb changed the title It should be possible to handle SIGINT that is raised inside looped prompt It should be possible to handle SIGINT that is raised inside a looped prompt Dec 24, 2024
@sigmaSd
Copy link
Contributor

sigmaSd commented Dec 24, 2024

You can use node readline

import * as readline from "node:readline/promises";
import process from "node:process";

const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout,
});
rl.addListener("SIGINT", () => {
  console.log("interrupted!"); // Never prints
  Deno.exit();
});

while (true) {
  const val = await rl.question("Prompt: "); // Hit Ctrl-c while in this prompt
  console.log(val); // logs null
}

prompt api is problematic in deno, its synchronous and it blocks all async tasks in deno, i imagine its not the case in browsers implementation

@kt3k
Copy link
Member

kt3k commented Dec 25, 2024

It seems like Example 1 should do something other than silently swallow the SIGINT function call.

It doesn't seem swallowed to me. If you break the loop, then the SIGINT handler works. The example runs the loop synchronously forever. There's no room for the handler to run.

Perhaps the handler should run after the user inputs text at the next prompt?

That is not possible by the single threaded nature of javascript.

@dsherret dsherret added the invalid what appeared to be an issue with Deno wasn't label Dec 28, 2024
@dsherret
Copy link
Member

As @kt3k pointed out, JavaScript is single threaded, so if you block the event loop by calling prompt then the other JavaScript code will never run.

Also, this is essentially a duplicate of #26919

@dsherret dsherret closed this as not planned Won't fix, can't repro, duplicate, stale Dec 28, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
invalid what appeared to be an issue with Deno wasn't
Projects
None yet
Development

No branches or pull requests

4 participants