diff options
author | Gus Caplan <me@gus.host> | 2019-10-11 15:53:41 -0700 |
---|---|---|
committer | Gus Caplan <me@gus.host> | 2019-10-13 15:45:36 -0700 |
commit | 545f7282d126ee43cf9cfeb66c83d0cbd2c60614 (patch) | |
tree | 8cd69c849fb60a6a84af8894c16f02c7723a7dcf | |
parent | ea3d5ff785e5f7b327942f9b47b3bd958e77c51f (diff) | |
download | android-node-v8-545f7282d126ee43cf9cfeb66c83d0cbd2c60614.tar.gz android-node-v8-545f7282d126ee43cf9cfeb66c83d0cbd2c60614.tar.bz2 android-node-v8-545f7282d126ee43cf9cfeb66c83d0cbd2c60614.zip |
src: implement v8 host weakref hooks
PR-URL: https://github.com/nodejs/node/pull/29874
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
-rw-r--r-- | .eslintrc.js | 1 | ||||
-rw-r--r-- | src/api/callback.cc | 3 | ||||
-rw-r--r-- | src/api/environment.cc | 12 | ||||
-rw-r--r-- | src/env-inl.h | 5 | ||||
-rw-r--r-- | src/env.cc | 16 | ||||
-rw-r--r-- | src/env.h | 5 | ||||
-rw-r--r-- | src/node_task_queue.cc | 2 | ||||
-rw-r--r-- | test/.eslintrc.yaml | 1 | ||||
-rw-r--r-- | test/parallel/test-finalization-group-error.js | 23 | ||||
-rw-r--r-- | test/parallel/test-finalization-group.js | 13 | ||||
-rw-r--r-- | test/parallel/test-weakref.js | 13 |
11 files changed, 94 insertions, 0 deletions
diff --git a/.eslintrc.js b/.eslintrc.js index cd59fd8339..8bdf2ad09a 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -317,5 +317,6 @@ module.exports = { TextEncoder: 'readable', TextDecoder: 'readable', queueMicrotask: 'readable', + globalThis: 'readable', }, }; diff --git a/src/api/callback.cc b/src/api/callback.cc index e6098d5921..6d4e28e1d9 100644 --- a/src/api/callback.cc +++ b/src/api/callback.cc @@ -100,6 +100,9 @@ void InternalCallbackScope::Close() { TickInfo* tick_info = env_->tick_info(); if (!env_->can_call_into_js()) return; + + OnScopeLeave weakref_cleanup([&]() { env_->RunWeakRefCleanup(); }); + if (!tick_info->has_tick_scheduled()) { MicrotasksScope::PerformCheckpoint(env_->isolate()); } diff --git a/src/api/environment.cc b/src/api/environment.cc index 2c0fe13063..3c5665a2d2 100644 --- a/src/api/environment.cc +++ b/src/api/environment.cc @@ -12,6 +12,7 @@ using errors::TryCatchScope; using v8::Array; using v8::Context; using v8::EscapableHandleScope; +using v8::FinalizationGroup; using v8::Function; using v8::HandleScope; using v8::Isolate; @@ -76,6 +77,15 @@ static MaybeLocal<Value> PrepareStackTraceCallback(Local<Context> context, return result; } +static void HostCleanupFinalizationGroupCallback( + Local<Context> context, Local<FinalizationGroup> group) { + Environment* env = Environment::GetCurrent(context); + if (env == nullptr) { + return; + } + env->RegisterFinalizationGroupForCleanup(group); +} + void* NodeArrayBufferAllocator::Allocate(size_t size) { if (zero_fill_field_ || per_process::cli_options->zero_fill_all_buffers) return UncheckedCalloc(size); @@ -203,6 +213,8 @@ void SetIsolateUpForNode(v8::Isolate* isolate, IsolateSettingCategories cat) { isolate->SetAllowWasmCodeGenerationCallback( AllowWasmCodeGenerationCallback); isolate->SetPromiseRejectCallback(task_queue::PromiseRejectCallback); + isolate->SetHostCleanupFinalizationGroupCallback( + HostCleanupFinalizationGroupCallback); v8::CpuProfiler::UseDetailedSourcePositionsForProfiling(isolate); break; default: diff --git a/src/env-inl.h b/src/env-inl.h index a2ab98d66d..d61ae8f8df 100644 --- a/src/env-inl.h +++ b/src/env-inl.h @@ -1115,6 +1115,11 @@ void Environment::RemoveCleanupHook(void (*fn)(void*), void* arg) { cleanup_hooks_.erase(search); } +inline void Environment::RegisterFinalizationGroupForCleanup( + v8::Local<v8::FinalizationGroup> group) { + cleanup_finalization_groups_.emplace_back(isolate(), group); +} + size_t CleanupHookCallback::Hash::operator()( const CleanupHookCallback& cb) const { return std::hash<void*>()(cb.arg_); diff --git a/src/env.cc b/src/env.cc index 2400785ea8..ff1868e75e 100644 --- a/src/env.cc +++ b/src/env.cc @@ -29,6 +29,7 @@ using v8::ArrayBuffer; using v8::Boolean; using v8::Context; using v8::EmbedderGraph; +using v8::FinalizationGroup; using v8::Function; using v8::FunctionTemplate; using v8::HandleScope; @@ -1051,6 +1052,21 @@ void Environment::AddArrayBufferAllocatorToKeepAliveUntilIsolateDispose( keep_alive_allocators_->insert(allocator); } +bool Environment::RunWeakRefCleanup() { + isolate()->ClearKeptObjects(); + + while (!cleanup_finalization_groups_.empty()) { + Local<FinalizationGroup> fg = + cleanup_finalization_groups_.front().Get(isolate()); + cleanup_finalization_groups_.pop_front(); + if (!FinalizationGroup::Cleanup(fg).FromMaybe(false)) { + return false; + } + } + + return true; +} + void AsyncRequest::Install(Environment* env, void* data, uv_async_cb target) { CHECK_NULL(async_); env_ = env; @@ -1129,6 +1129,9 @@ class Environment : public MemoryRetainer { void AtExit(void (*cb)(void* arg), void* arg); void RunAtExitCallbacks(); + void RegisterFinalizationGroupForCleanup(v8::Local<v8::FinalizationGroup> fg); + bool RunWeakRefCleanup(); + // Strings and private symbols are shared across shared contexts // The getters simply proxy to the per-isolate primitive. #define VP(PropertyName, StringValue) V(v8::Private, PropertyName) @@ -1335,6 +1338,8 @@ class Environment : public MemoryRetainer { uint64_t thread_id_; std::unordered_set<worker::Worker*> sub_worker_contexts_; + std::deque<v8::Global<v8::FinalizationGroup>> cleanup_finalization_groups_; + static void* const kNodeContextTagPtr; static int const kNodeContextTag; diff --git a/src/node_task_queue.cc b/src/node_task_queue.cc index 3b00cbc600..ef1aff6cd4 100644 --- a/src/node_task_queue.cc +++ b/src/node_task_queue.cc @@ -43,6 +43,8 @@ static void EnqueueMicrotask(const FunctionCallbackInfo<Value>& args) { // Should be in sync with runNextTicks in internal/process/task_queues.js bool RunNextTicksNative(Environment* env) { + OnScopeLeave weakref_cleanup([&]() { env->RunWeakRefCleanup(); }); + TickInfo* tick_info = env->tick_info(); if (!tick_info->has_tick_scheduled() && !tick_info->has_rejection_to_warn()) MicrotasksScope::PerformCheckpoint(env->isolate()); diff --git a/test/.eslintrc.yaml b/test/.eslintrc.yaml index 8a36ac522a..1dd006566a 100644 --- a/test/.eslintrc.yaml +++ b/test/.eslintrc.yaml @@ -34,3 +34,4 @@ globals: BigInt64Array: false BigUint64Array: false SharedArrayBuffer: false + globalThis: false diff --git a/test/parallel/test-finalization-group-error.js b/test/parallel/test-finalization-group-error.js new file mode 100644 index 0000000000..0754370f2d --- /dev/null +++ b/test/parallel/test-finalization-group-error.js @@ -0,0 +1,23 @@ +'use strict'; + +// Flags: --expose-gc --harmony-weak-refs + +const common = require('../common'); +const assert = require('assert'); + +const g = new globalThis.FinalizationGroup(common.mustCallAtLeast(() => { + throw new Error('test'); +}, 1)); +g.register({}, 42); + +setTimeout(() => { + globalThis.gc(); + assert.throws(() => { + g.cleanupSome(); + }, { + name: 'Error', + message: 'test', + }); +}, 200); + +process.on('uncaughtException', common.mustCall()); diff --git a/test/parallel/test-finalization-group.js b/test/parallel/test-finalization-group.js new file mode 100644 index 0000000000..3e6b9d72e3 --- /dev/null +++ b/test/parallel/test-finalization-group.js @@ -0,0 +1,13 @@ +'use strict'; + +// Flags: --expose-gc --harmony-weak-refs + +const common = require('../common'); + +const g = new globalThis.FinalizationGroup(common.mustCallAtLeast(1)); +g.register({}, 42); + +setTimeout(() => { + globalThis.gc(); + g.cleanupSome(); +}, 200); diff --git a/test/parallel/test-weakref.js b/test/parallel/test-weakref.js new file mode 100644 index 0000000000..9dac846376 --- /dev/null +++ b/test/parallel/test-weakref.js @@ -0,0 +1,13 @@ +'use strict'; + +// Flags: --expose-gc --harmony-weak-refs + +require('../common'); +const assert = require('assert'); + +const w = new globalThis.WeakRef({}); + +setTimeout(() => { + globalThis.gc(); + assert.strictEqual(w.deref(), undefined); +}, 200); |