From bac07596f36153adf0955fcf97571680e9e24ce3 Mon Sep 17 00:00:00 2001 From: Archkon <180910180+Archkon@users.noreply.github.com> Date: Sat, 4 Jul 2026 16:51:21 +0800 Subject: [PATCH] src: avoid V8 worker hang during platform shutdown Node could hang during shutdown while joining V8 worker threads if a worker was parked waiting for foreground GC work. This can happen when a V8 background task requests GC from the main isolate thread during process exit, after Node has stopped processing foreground tasks but before the platform worker pool is joined. Flush foreground tasks for registered Node isolates before shutting down the worker task runner so pending GC work can run and release parked workers. Signed-off-by: Archkon <180910180+Archkon@users.noreply.github.com> --- src/node_platform.cc | 25 +++++++++++++++++++++++++ src/node_platform.h | 1 + 2 files changed, 26 insertions(+) diff --git a/src/node_platform.cc b/src/node_platform.cc index 197102068b74f4..57ab6a616f144c 100644 --- a/src/node_platform.cc +++ b/src/node_platform.cc @@ -526,6 +526,12 @@ void NodePlatform::AddIsolateFinishedCallback(Isolate* isolate, void NodePlatform::Shutdown() { if (has_shut_down_) return; has_shut_down_ = true; + + // V8 background tasks may be parked waiting for foreground GC work before + // they can return from the worker thread task. + while (FlushForegroundTasksForAllIsolates()) { + } + worker_thread_task_runner_->Shutdown(); { @@ -717,6 +723,25 @@ bool NodePlatform::FlushForegroundTasks(Isolate* isolate) { return per_isolate->FlushForegroundTasksInternal(); } +bool NodePlatform::FlushForegroundTasksForAllIsolates() { + std::vector> per_isolates; + { + Mutex::ScopedLock lock(per_isolate_mutex_); + per_isolates.reserve(per_isolate_.size()); + for (const auto& entry : per_isolate_) { + if (entry.second.second) { + per_isolates.push_back(entry.second.second); + } + } + } + + bool did_work = false; + for (const auto& per_isolate : per_isolates) { + did_work |= per_isolate->FlushForegroundTasksInternal(); + } + return did_work; +} + std::unique_ptr NodePlatform::CreateJobImpl( v8::TaskPriority priority, std::unique_ptr job_task, diff --git a/src/node_platform.h b/src/node_platform.h index f47e2a46b66b84..bc2cc58a16a6ba 100644 --- a/src/node_platform.h +++ b/src/node_platform.h @@ -266,6 +266,7 @@ class NodePlatform : public MultiIsolatePlatform { private: IsolatePlatformDelegate* ForIsolate(v8::Isolate* isolate); std::shared_ptr ForNodeIsolate(v8::Isolate* isolate); + bool FlushForegroundTasksForAllIsolates(); Mutex per_isolate_mutex_; using DelegatePair = std::pair