diff --git a/src/api/callback.cc b/src/api/callback.cc index 1287eb466fddd3..6a62937246803a 100644 --- a/src/api/callback.cc +++ b/src/api/callback.cc @@ -97,10 +97,11 @@ void InternalCallbackScope::Close() { if (closed_) return; closed_ = true; + if (!env_->can_call_into_js()) return; + Isolate* isolate = env_->isolate(); auto idle = OnScopeLeave([&]() { isolate->SetIdle(true); }); - if (!env_->can_call_into_js()) return; auto perform_stopping_check = [&]() { if (env_->is_stopping()) { MarkAsFailed(); diff --git a/test/parallel/test-unhandled-exception-with-worker-inuse.js b/test/parallel/test-unhandled-exception-with-worker-inuse.js new file mode 100644 index 00000000000000..fd6d60da7f7a9e --- /dev/null +++ b/test/parallel/test-unhandled-exception-with-worker-inuse.js @@ -0,0 +1,29 @@ +'use strict'; +const common = require('../common'); + +// https://github.com/nodejs/node/issues/45421 +// +// Check that node will not call v8::Isolate::SetIdle() when exiting +// due to an unhandled exception, otherwise the assertion(enabled in +// debug build only) in the SetIdle() will fail. +// +// The root cause of this issue is that before PerIsolateMessageListener() +// is invoked by v8, v8 preserves the vm state, which is JS. However, +// SetIdle() requires the vm state is either EXTERNAL or IDLE when embedder +// calling it. + +if (process.argv[2] === 'child') { + const { Worker } = require('worker_threads'); + new Worker('', { eval: true }); + throw new Error('xxx'); +} else { + const assert = require('assert'); + const { spawnSync } = require('child_process'); + const result = spawnSync(process.execPath, [__filename, 'child']); + + const stderr = result.stderr.toString().trim(); + // Expect error message to be preserved + assert.match(stderr, /xxx/); + // Expect no crash + assert.ok(!common.nodeProcessAborted(result.status, result.signal), stderr); +}