-
-
Notifications
You must be signed in to change notification settings - Fork 154
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
Improper handling of graceful exits #95
Comments
I investigated this and turns out it's a really difficult problem. I'm currently convinced there isn't a good solution in Node.js. Here are some approaches I considered: 1. Relay kill signals from parent to child processimport { constants } from 'os'
const uncatchableSignals = ['SIGKILL', 'SIGSTOP']
for (const signal of Object.keys(constants.signals)) {
if (!uncatchableSignals.includes(signal)) {
process.on(signal, () => {
child.kill(signal)
})
}
} This works fine when a kill is sent directly to the parent process. But in an interactive shell, pressing Ctrl+C sends a Additionally, this approach prevents graceful exists on Windows. Since Windows doesn't have signals, Node.js listens for Ctrl+C on
FWIW this is how 2. Ignore kill signals on parent processimport { constants } from 'os'
const noop = () => {}
const uncatchableSignals = ['SIGKILL', 'SIGSTOP']
for (const signal of Object.keys(constants.signals)) {
if (!uncatchableSignals.includes(signal)) {
// Let shell send signals to child directly
process.on(signal, noop)
}
} To prevent the signal from being received twice, I figured maybe we can ignore signals on the parent and let the shell send them to the child directly (e.g. on Ctrl+C). However, this disables programmatic 3. Detached child processimport { spawn } from 'child_process'
spawn(process.execPath, process.argv.slice(1), {
stdio: 'inherit',
detached: true
}) On Linux/Mac, detaching the child process while inherting the This was convenient because the parent can relay the kill signal to the child and it wouldn't be sent again by the shell. However, on Windows, detaching the child seemed to prevent 4. Only relay kill signal if Ctrl+C is not pressedBy detecting Ctrl+C (specifically the keypress), we would be able to distinguish kill signals from interactive shells vs programmatic ones, and only relay signals to the child if it's programmatic. However, detecting Ctrl+C requires This approach doesn't seem possible. I investigated how npm and pnpm handle this, and seems they both do a simple relay on npm: https://github.com/npm/run-script/blob/8e08311358a9f7c361e191b728eaada53eba607b/lib/signal-manager.js#L11 As stated in Approach 1, this approach has the drawback of the child process receiving the signal twice when triggered via shell shortcut (e.g. Ctrl+C), and it terminates the process immediately on Windows. However, with npm, it seems to inadvertently prevent that by spawning a shell. So, pressing Ctrl+C on Windows prompts "Terminate batch job (Y/N)?", allowing the child to run the Since this approach seems widely adopted, I'm going to move forward with it despite it's limitations. |
@privatenumber thanks for spending time looking into it. Given the circumstances, I think it's reasonable for the child process to handle duplicated signals by not initiating another shutdown procedure if one is already in progress. Thanks again for your hard work! |
I actually managed to prevent double signal receiving and also preserve Windows support for Ctrl + C. Will release shortly. |
Bug description
tsx
does not wait for script to exit gracefully before terminating the process.Reproduction
Let's say you have
main.ts
as the following:Running
main.ts
vianode --no-warnings --loader tsx main.ts
will produce the correct behavior, i.e. pressingCTRL+C
will signal the script to start cleaning up and then exit gracefully.On the other hand, when running via
tsx main.ts
, the process will exit abruptly when signaled without waiting for the script to completely finish cleaning up.Environment
System: OS: Linux 5.15 Ubuntu 22.04.1 LTS 22.04.1 LTS (Jammy Jellyfish) CPU: (4) x64 Intel(R) Core(TM) i7-7500U CPU @ 2.70GHz Memory: 10.00 GB / 15.52 GB Container: Yes Shell: 5.1.16 - /bin/bash Binaries: Node: 18.7.0 - /usr/local/node/bin/node npm: 8.15.0 - /usr/local/node/bin/npm npmPackages: tsx: ^3.8.2 => 3.8.2
Can you contribute a fix?
The text was updated successfully, but these errors were encountered: