diff --git a/src/api/environment.cc b/src/api/environment.cc index 7e30a35ad7e611..8d7ecad56175ff 100644 --- a/src/api/environment.cc +++ b/src/api/environment.cc @@ -512,13 +512,6 @@ void FreeEnvironment(Environment* env) { RunAtExit(env); } - // This call needs to be made while the `Environment` is still alive - // because we assume that it is available for async tracking in the - // NodePlatform implementation. - MultiIsolatePlatform* platform = env->isolate_data()->platform(); - if (platform != nullptr) - platform->DrainTasks(isolate); - delete env; } diff --git a/src/node_main_instance.cc b/src/node_main_instance.cc index 64ab1375708c00..8420db61a8c182 100644 --- a/src/node_main_instance.cc +++ b/src/node_main_instance.cc @@ -65,6 +65,13 @@ NodeMainInstance::~NodeMainInstance() { if (isolate_params_ == nullptr) { return; } + +#ifdef DEBUG + // node::Environment has been disposed and no JavaScript Execution is allowed + // at this point. + Isolate::DisallowJavascriptExecutionScope disallow_js( + isolate_, Isolate::DisallowJavascriptExecutionScope::CRASH_ON_FAILURE); +#endif // This should only be done on a main instance that owns its isolate. // IsolateData must be freed before UnregisterIsolate() is called. isolate_data_.reset(); diff --git a/src/node_platform.cc b/src/node_platform.cc index 927fdddb8d9a1a..97cf6cb840bab5 100644 --- a/src/node_platform.cc +++ b/src/node_platform.cc @@ -424,6 +424,11 @@ void PerIsolatePlatformData::RunForegroundTask(std::unique_ptr task) { InternalCallbackScope::kNoFlags); task->Run(); } else { + // When the Environment was freed, the tasks of the Isolate should also be + // canceled by `NodePlatform::UnregisterIsolate`. However, if the embedder + // request to run the foreground task after the Environment was freed, run + // the task without InternalCallbackScope. + // The task is moved out of InternalCallbackScope if env is not available. // This is a required else block, and should not be removed. // See comment: https://github.com/nodejs/node/pull/34688#pullrequestreview-463867489 diff --git a/test/parallel/test-finalization-registry-shutdown.js b/test/parallel/test-finalization-registry-shutdown.js new file mode 100644 index 00000000000000..f896aa2f285c75 --- /dev/null +++ b/test/parallel/test-finalization-registry-shutdown.js @@ -0,0 +1,23 @@ +// Flags: --expose-gc +'use strict'; +const common = require('../common'); + +// This test verifies that when a V8 FinalizationRegistryCleanupTask is queue +// at the last moment when JavaScript can be executed, the callback of a +// FinalizationRegistry will not be invoked and the process should exit +// normally. + +const reg = new FinalizationRegistry( + common.mustNotCall('This FinalizationRegistry callback should never be called')); + +function register() { + // Create a temporary object in a new function scope to allow it to be GC-ed. + reg.register({}); +} + +process.on('exit', () => { + // This is the final chance to execute JavaScript. + register(); + // Queue a FinalizationRegistryCleanupTask by a testing gc request. + global.gc(); +});