Skip to content

Commit 545f728

Browse files
committedOct 13, 2019
src: implement v8 host weakref hooks
PR-URL: #29874 Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: Ben Noordhuis <[email protected]>
1 parent ea3d5ff commit 545f728

11 files changed

+94
-0
lines changed
 

‎.eslintrc.js

+1
Original file line numberDiff line numberDiff line change
@@ -317,5 +317,6 @@ module.exports = {
317317
TextEncoder: 'readable',
318318
TextDecoder: 'readable',
319319
queueMicrotask: 'readable',
320+
globalThis: 'readable',
320321
},
321322
};

‎src/api/callback.cc

+3
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,9 @@ void InternalCallbackScope::Close() {
100100
TickInfo* tick_info = env_->tick_info();
101101

102102
if (!env_->can_call_into_js()) return;
103+
104+
OnScopeLeave weakref_cleanup([&]() { env_->RunWeakRefCleanup(); });
105+
103106
if (!tick_info->has_tick_scheduled()) {
104107
MicrotasksScope::PerformCheckpoint(env_->isolate());
105108
}

‎src/api/environment.cc

+12
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ using errors::TryCatchScope;
1212
using v8::Array;
1313
using v8::Context;
1414
using v8::EscapableHandleScope;
15+
using v8::FinalizationGroup;
1516
using v8::Function;
1617
using v8::HandleScope;
1718
using v8::Isolate;
@@ -76,6 +77,15 @@ static MaybeLocal<Value> PrepareStackTraceCallback(Local<Context> context,
7677
return result;
7778
}
7879

80+
static void HostCleanupFinalizationGroupCallback(
81+
Local<Context> context, Local<FinalizationGroup> group) {
82+
Environment* env = Environment::GetCurrent(context);
83+
if (env == nullptr) {
84+
return;
85+
}
86+
env->RegisterFinalizationGroupForCleanup(group);
87+
}
88+
7989
void* NodeArrayBufferAllocator::Allocate(size_t size) {
8090
if (zero_fill_field_ || per_process::cli_options->zero_fill_all_buffers)
8191
return UncheckedCalloc(size);
@@ -203,6 +213,8 @@ void SetIsolateUpForNode(v8::Isolate* isolate, IsolateSettingCategories cat) {
203213
isolate->SetAllowWasmCodeGenerationCallback(
204214
AllowWasmCodeGenerationCallback);
205215
isolate->SetPromiseRejectCallback(task_queue::PromiseRejectCallback);
216+
isolate->SetHostCleanupFinalizationGroupCallback(
217+
HostCleanupFinalizationGroupCallback);
206218
v8::CpuProfiler::UseDetailedSourcePositionsForProfiling(isolate);
207219
break;
208220
default:

‎src/env-inl.h

+5
Original file line numberDiff line numberDiff line change
@@ -1115,6 +1115,11 @@ void Environment::RemoveCleanupHook(void (*fn)(void*), void* arg) {
11151115
cleanup_hooks_.erase(search);
11161116
}
11171117

1118+
inline void Environment::RegisterFinalizationGroupForCleanup(
1119+
v8::Local<v8::FinalizationGroup> group) {
1120+
cleanup_finalization_groups_.emplace_back(isolate(), group);
1121+
}
1122+
11181123
size_t CleanupHookCallback::Hash::operator()(
11191124
const CleanupHookCallback& cb) const {
11201125
return std::hash<void*>()(cb.arg_);

‎src/env.cc

+16
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ using v8::ArrayBuffer;
2929
using v8::Boolean;
3030
using v8::Context;
3131
using v8::EmbedderGraph;
32+
using v8::FinalizationGroup;
3233
using v8::Function;
3334
using v8::FunctionTemplate;
3435
using v8::HandleScope;
@@ -1051,6 +1052,21 @@ void Environment::AddArrayBufferAllocatorToKeepAliveUntilIsolateDispose(
10511052
keep_alive_allocators_->insert(allocator);
10521053
}
10531054

1055+
bool Environment::RunWeakRefCleanup() {
1056+
isolate()->ClearKeptObjects();
1057+
1058+
while (!cleanup_finalization_groups_.empty()) {
1059+
Local<FinalizationGroup> fg =
1060+
cleanup_finalization_groups_.front().Get(isolate());
1061+
cleanup_finalization_groups_.pop_front();
1062+
if (!FinalizationGroup::Cleanup(fg).FromMaybe(false)) {
1063+
return false;
1064+
}
1065+
}
1066+
1067+
return true;
1068+
}
1069+
10541070
void AsyncRequest::Install(Environment* env, void* data, uv_async_cb target) {
10551071
CHECK_NULL(async_);
10561072
env_ = env;

‎src/env.h

+5
Original file line numberDiff line numberDiff line change
@@ -1129,6 +1129,9 @@ class Environment : public MemoryRetainer {
11291129
void AtExit(void (*cb)(void* arg), void* arg);
11301130
void RunAtExitCallbacks();
11311131

1132+
void RegisterFinalizationGroupForCleanup(v8::Local<v8::FinalizationGroup> fg);
1133+
bool RunWeakRefCleanup();
1134+
11321135
// Strings and private symbols are shared across shared contexts
11331136
// The getters simply proxy to the per-isolate primitive.
11341137
#define VP(PropertyName, StringValue) V(v8::Private, PropertyName)
@@ -1335,6 +1338,8 @@ class Environment : public MemoryRetainer {
13351338
uint64_t thread_id_;
13361339
std::unordered_set<worker::Worker*> sub_worker_contexts_;
13371340

1341+
std::deque<v8::Global<v8::FinalizationGroup>> cleanup_finalization_groups_;
1342+
13381343
static void* const kNodeContextTagPtr;
13391344
static int const kNodeContextTag;
13401345

‎src/node_task_queue.cc

+2
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ static void EnqueueMicrotask(const FunctionCallbackInfo<Value>& args) {
4343

4444
// Should be in sync with runNextTicks in internal/process/task_queues.js
4545
bool RunNextTicksNative(Environment* env) {
46+
OnScopeLeave weakref_cleanup([&]() { env->RunWeakRefCleanup(); });
47+
4648
TickInfo* tick_info = env->tick_info();
4749
if (!tick_info->has_tick_scheduled() && !tick_info->has_rejection_to_warn())
4850
MicrotasksScope::PerformCheckpoint(env->isolate());

‎test/.eslintrc.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,4 @@ globals:
3434
BigInt64Array: false
3535
BigUint64Array: false
3636
SharedArrayBuffer: false
37+
globalThis: false
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
'use strict';
2+
3+
// Flags: --expose-gc --harmony-weak-refs
4+
5+
const common = require('../common');
6+
const assert = require('assert');
7+
8+
const g = new globalThis.FinalizationGroup(common.mustCallAtLeast(() => {
9+
throw new Error('test');
10+
}, 1));
11+
g.register({}, 42);
12+
13+
setTimeout(() => {
14+
globalThis.gc();
15+
assert.throws(() => {
16+
g.cleanupSome();
17+
}, {
18+
name: 'Error',
19+
message: 'test',
20+
});
21+
}, 200);
22+
23+
process.on('uncaughtException', common.mustCall());
+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
'use strict';
2+
3+
// Flags: --expose-gc --harmony-weak-refs
4+
5+
const common = require('../common');
6+
7+
const g = new globalThis.FinalizationGroup(common.mustCallAtLeast(1));
8+
g.register({}, 42);
9+
10+
setTimeout(() => {
11+
globalThis.gc();
12+
g.cleanupSome();
13+
}, 200);

‎test/parallel/test-weakref.js

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
'use strict';
2+
3+
// Flags: --expose-gc --harmony-weak-refs
4+
5+
require('../common');
6+
const assert = require('assert');
7+
8+
const w = new globalThis.WeakRef({});
9+
10+
setTimeout(() => {
11+
globalThis.gc();
12+
assert.strictEqual(w.deref(), undefined);
13+
}, 200);

0 commit comments

Comments
 (0)