diff --git a/docs/breaking-changes.md b/docs/breaking-changes.md index 1b605d9771..4a8830667c 100644 --- a/docs/breaking-changes.md +++ b/docs/breaking-changes.md @@ -36,6 +36,16 @@ process.on('unhandledRejection', () => { }) ``` +### Behavior Changed: `process.exit()` kills utility process synchronously + +Calling `process.exit()` in a utility process will now kill the utility process synchronously. +This brings the behavior of `process.exit()` in line with Node.js behavior. + +Please refer to the +[Node.js docs](https://nodejs.org/docs/latest-v22.x/api/process.html#processexitcode) and +[PR #45690](https://github.com/electron/electron/pull/45690) to understand the potential +implications of that, e.g., when calling `console.log()` before `process.exit()`. + ### Behavior Changed: WebUSB and WebSerial Blocklist Support [WebUSB](https://developer.mozilla.org/en-US/docs/Web/API/WebUSB_API) and [Web Serial](https://developer.mozilla.org/en-US/docs/Web/API/Web_Serial_API) now support the [WebUSB Blocklist](https://wicg.github.io/webusb/#blocklist) and [Web Serial Blocklist](https://wicg.github.io/serial/#blocklist) used by Chromium and outlined in their respective specifications. diff --git a/shell/services/node/node_service.cc b/shell/services/node/node_service.cc index fd42dedad3..7acca3c991 100644 --- a/shell/services/node/node_service.cc +++ b/shell/services/node/node_service.cc @@ -9,6 +9,7 @@ #include "base/command_line.h" #include "base/no_destructor.h" +#include "base/process/process.h" #include "base/strings/utf_string_conversions.h" #include "electron/mas.h" #include "services/network/public/cpp/wrapper_shared_url_loader_factory.h" @@ -99,8 +100,6 @@ NodeService::~NodeService() { ParentPort::GetInstance()->Close(); js_env_->DestroyMicrotasksRunner(); node::Stop(node_env_.get(), node::StopFlags::kDoNotTerminateIsolate); - } - if (g_client_remote.is_bound()) { g_client_remote.reset(); } } @@ -147,12 +146,12 @@ void NodeService::Initialize( node::SetProcessExitHandler( node_env_.get(), [this](node::Environment* env, int exit_code) { // Destroy node platform. - env->set_trace_sync_io(false); + node_env_stopped_ = true; ParentPort::GetInstance()->Close(); js_env_->DestroyMicrotasksRunner(); - node::Stop(env, node::StopFlags::kDoNotTerminateIsolate); - node_env_stopped_ = true; + g_client_remote.reset(); receiver_.ResetWithReason(exit_code, "process_exit_termination"); + node::DefaultProcessExitHandler(env, exit_code); }); node_env_->set_trace_sync_io(node_env_->options()->trace_sync_io); diff --git a/spec/api-utility-process-spec.ts b/spec/api-utility-process-spec.ts index e365a8fd78..5a132a42ec 100644 --- a/spec/api-utility-process-spec.ts +++ b/spec/api-utility-process-spec.ts @@ -129,6 +129,26 @@ describe('utilityProcess module', () => { expect(code).to.equal(exitCode); }); + it('does not run JS after process.exit is called', async () => { + const file = path.join(os.tmpdir(), `no-js-after-exit-log-${Math.random()}`); + const child = utilityProcess.fork(path.join(fixturesPath, 'no-js-after-exit.js'), [`--testPath=${file}`]); + const [code] = await once(child, 'exit'); + expect(code).to.equal(1); + let handle = null; + const lines = []; + try { + handle = await fs.open(file); + for await (const line of handle.readLines()) { + lines.push(line); + } + } finally { + await handle?.close(); + await fs.rm(file, { force: true }); + } + expect(lines.length).to.equal(1); + expect(lines[0]).to.equal('before exit'); + }); + // 32-bit system will not have V8 Sandbox enabled. // WoA testing does not have VS toolchain configured to build native addons. ifit(process.arch !== 'ia32' && process.arch !== 'arm' && !isWindowsOnArm)('emits \'error\' when fatal error is triggered from V8', async () => { diff --git a/spec/fixtures/api/utility-process/no-js-after-exit.js b/spec/fixtures/api/utility-process/no-js-after-exit.js new file mode 100644 index 0000000000..644019006b --- /dev/null +++ b/spec/fixtures/api/utility-process/no-js-after-exit.js @@ -0,0 +1,7 @@ +const { writeFileSync } = require('node:fs'); + +const arg = process.argv[2]; +const file = arg.split('=')[1]; +writeFileSync(file, 'before exit'); +process.exit(1); +writeFileSync(file, 'after exit');