execa icon indicating copy to clipboard operation
execa copied to clipboard

Subprocess is considered as failed exit when using `process.kill` on windows only

Open huang-julien opened this issue 1 year ago • 3 comments

Reproduction

https://github.com/huang-julien/execa-repro

Steps

  • clone it on windows
  • run index.js with WSL
  • it exit after 5 sec without issue
  • run again but with powershell or cmd
  • it crash after 5 sec (terminated with SIGTERM)

Description

Hello :wave:

This is an issue we detected on https://github.com/nuxt/test-utils/pull/848

On windows, with using process.exit() without exitCode, the exitCode received by execa is null while it is 0 on linux.

huang-julien avatar May 26 '24 21:05 huang-julien

Hi @huang-julien,

Thanks for reporting this!

I cannot easily setup WSL on my machine at the very moment, so we might need a few back-and-forth going through your reproduction example. Thanks for putting this up!

My first question would be: does the example reproduce without setting up the HTTP server?

ehmicky avatar May 26 '24 21:05 ehmicky

I reduced the repro with a simple timeout :) .

WSL isn't mandatory, you can use linux. It's just that I don't have dual boot linux/windows 😅

Weirdly, execa receive 0 and null for exitCode and signal in WSL/linux but gets null and 'SIGTERM' in windows

huang-julien avatar May 26 '24 21:05 huang-julien

Thanks!

What's happening is the following: Windows does not support Unix-style signals. Node.js emulates a handful of signals, but it has some limitations. You can find more details here and there. This behavior is inherited by Execa since we rely on Node.js child_process core module.

Links to the code for subprocess.kill():

And for process.on(signal):

So, on Windows (without WSL), process.on('SIGTERM') is a noop. You should be able to experience this yourself by looking at the stdout property on the Execa result, which should contain 'SIGTERM signal received' when using WSL, but not when using cmd.exe or Powershell.

On Unix (or WSL), process.on('SIGTERM') is run, and since you call process.exit(), the subprocess has a successful exit, which means exitCode is 0 and signal is undefined. On Windows (non-WSL), process.on('SIGTERM') is not run, and the subprocess is terminated right away, which means exitCode is undefined and signal is 'SIGTERM'.

This is the expected behavior. The difference is due to the fact that Unix and Windows implement IPC differently. Node.js/libuv does some work to try to bridge those differences, but it is unfortunately limited.

The reason this behavior is now showing up with Execa 9 and not 8: I am not completely sure, but this is probably due to a bug fix landed by Execa 9. We fixed a bunch of related bugs in that release, and it's possible the new behavior, which is the correct one, might have been previously erroneous.

ehmicky avatar May 27 '24 00:05 ehmicky

Thanks for posting this issue, as it gave me the idea to:

  • Add a way to gracefully terminate in a cross-platform way, which will be available in the upcoming release. See docs
  • Better document signal handling. Docs

@huang-julien Based on the above, do you think this issue should be closed, or is there something else that you think might be a pending bug?

ehmicky avatar Jun 03 '24 18:06 ehmicky

This would be wonderful, thank you !

We still have a process pending with subprocess.kill(0) but i think it may be worth checking that after the upcoming release. This issue can probably closed.

huang-julien avatar Jun 04 '24 07:06 huang-julien

The new features mentioned in my previous comment have just been released in 9.2.0.

ehmicky avatar Jun 06 '24 18:06 ehmicky