Skip to content

Commit 0cd1a72

Browse files
committed
src: avoid draining tasks at FreeEnvironment
At the point of `FreeEnvironment` and onwards, no JavaScript execution associated with the Environment should be triggered. Avoid draining platform tasks that can trigger JavaScript execution in `FreeEnvironment`. The holder of `node::Environment` should immediately call `node::MultiIsolatePlatform::UnregisterIsolate` and `v8::Isolate::Dispose` to cancel pending foreground tasks and join concurrent tasks after the environment was freed. `NodePlatform` can properly handle the case in `RunForegroundTask` when an Isolate out-lives its associated `node::Environment`.
1 parent 89ddc98 commit 0cd1a72

File tree

4 files changed

+35
-7
lines changed

4 files changed

+35
-7
lines changed

src/api/environment.cc

-7
Original file line numberDiff line numberDiff line change
@@ -512,13 +512,6 @@ void FreeEnvironment(Environment* env) {
512512
RunAtExit(env);
513513
}
514514

515-
// This call needs to be made while the `Environment` is still alive
516-
// because we assume that it is available for async tracking in the
517-
// NodePlatform implementation.
518-
MultiIsolatePlatform* platform = env->isolate_data()->platform();
519-
if (platform != nullptr)
520-
platform->DrainTasks(isolate);
521-
522515
delete env;
523516
}
524517

src/node_main_instance.cc

+7
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,13 @@ NodeMainInstance::~NodeMainInstance() {
6565
if (isolate_params_ == nullptr) {
6666
return;
6767
}
68+
69+
#ifdef DEBUG
70+
// node::Environment has been disposed and no JavaScript Execution is allowed
71+
// at this point.
72+
Isolate::DisallowJavascriptExecutionScope disallow_js(
73+
isolate_, Isolate::DisallowJavascriptExecutionScope::CRASH_ON_FAILURE);
74+
#endif
6875
// This should only be done on a main instance that owns its isolate.
6976
// IsolateData must be freed before UnregisterIsolate() is called.
7077
isolate_data_.reset();

src/node_platform.cc

+5
Original file line numberDiff line numberDiff line change
@@ -424,6 +424,11 @@ void PerIsolatePlatformData::RunForegroundTask(std::unique_ptr<Task> task) {
424424
InternalCallbackScope::kNoFlags);
425425
task->Run();
426426
} else {
427+
// When the Environment was freed, the tasks of the Isolate should also be
428+
// canceled by `NodePlatform::UnregisterIsolate`. However, if the embedder
429+
// request to run the foreground task after the Environment was freed, run
430+
// the task without InternalCallbackScope.
431+
427432
// The task is moved out of InternalCallbackScope if env is not available.
428433
// This is a required else block, and should not be removed.
429434
// See comment: https://github.com/nodejs/node/pull/34688#pullrequestreview-463867489
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// Flags: --expose-gc
2+
'use strict';
3+
const common = require('../common');
4+
5+
// This test verifies that when a V8 FinalizationRegistryCleanupTask is queue
6+
// at the last moment when JavaScript can be executed, the callback of a
7+
// FinalizationRegistry will not be invoked and the process should exit
8+
// normally.
9+
10+
const reg = new FinalizationRegistry(
11+
common.mustNotCall('This FinalizationRegistry callback should never be called'));
12+
13+
function register() {
14+
// Create a temporary object in a new function scope to allow it to be GC-ed.
15+
reg.register({});
16+
}
17+
18+
process.on('exit', () => {
19+
// This is the final chance to execute JavaScript.
20+
register();
21+
// Queue a FinalizationRegistryCleanupTask by a testing gc request.
22+
global.gc();
23+
});

0 commit comments

Comments
 (0)