diff --git a/src/env.cc b/src/env.cc index 2cbbd14e713b9b..6b4b458e738d5f 100644 --- a/src/env.cc +++ b/src/env.cc @@ -276,9 +276,11 @@ Environment::~Environment() { TRACE_EVENT_NESTABLE_ASYNC_END0( TRACING_CATEGORY_NODE1(environment), "Environment", this); - // Dereference all addons that were loaded into this environment. - for (binding::DLib& addon : loaded_addons_) { - addon.Close(); + if (!is_main_thread()) { + // Dereference all addons that were loaded into this environment. + for (binding::DLib& addon : loaded_addons_) { + addon.Close(); + } } } diff --git a/test/addons/worker-addon/binding.cc b/test/addons/worker-addon/binding.cc index 1fb85ae230eb5f..7e32a58175d33a 100644 --- a/test/addons/worker-addon/binding.cc +++ b/test/addons/worker-addon/binding.cc @@ -3,6 +3,7 @@ #include #include #include +#include using v8::Context; using v8::HandleScope; @@ -41,6 +42,16 @@ void Initialize(Local exports, const_cast(static_cast("cleanup"))); node::AddEnvironmentCleanupHook(context->GetIsolate(), Dummy, nullptr); node::RemoveEnvironmentCleanupHook(context->GetIsolate(), Dummy, nullptr); + + if (getenv("addExtraItemToEventLoop") != nullptr) { + // Add an item to the event loop that we do not clean up in order to make + // sure that for the main thread, this addon's memory persists even after + // the Environment instance has been destroyed. + static uv_async_t extra_async; + uv_loop_t* loop = node::GetCurrentEventLoop(context->GetIsolate()); + uv_async_init(loop, &extra_async, [](uv_async_t*) {}); + uv_unref(reinterpret_cast(&extra_async)); + } } NODE_MODULE_CONTEXT_AWARE(NODE_GYP_MODULE_NAME, Initialize) diff --git a/test/addons/worker-addon/test.js b/test/addons/worker-addon/test.js index b6d962c0868741..7fb7c1a87aa903 100644 --- a/test/addons/worker-addon/test.js +++ b/test/addons/worker-addon/test.js @@ -6,12 +6,19 @@ const path = require('path'); const { Worker } = require('worker_threads'); const binding = path.resolve(__dirname, `./build/${common.buildType}/binding`); -if (process.argv[2] === 'child') { +if (process.argv[2] === 'worker') { new Worker(`require(${JSON.stringify(binding)});`, { eval: true }); -} else { + return; +} else if (process.argv[2] === 'main-thread') { + process.env.addExtraItemToEventLoop = 'yes'; + require(binding); + return; +} + +for (const test of ['worker', 'main-thread']) { const proc = child_process.spawnSync(process.execPath, [ __filename, - 'child' + test ]); assert.strictEqual(proc.stderr.toString(), ''); assert.strictEqual(proc.stdout.toString(), 'ctor cleanup dtor');