Skip to content

Commit 61fc754

Browse files
addaleaxtargos
authored andcommitted
inspector: properly shut down uv_async_t
Closing in the Agent destructor is too late, because that happens when the Environment is destroyed, not when libuv handles are closed. This fixes a situation in which the same libuv loop is re-used for multiple Environment instances sequentially, e.g. in our cctest. PR-URL: #30612 Reviewed-By: Eugene Ostroukhov <[email protected]> Reviewed-By: Colin Ihrig <[email protected]> Reviewed-By: David Carlier <[email protected]> Reviewed-By: Ben Noordhuis <[email protected]>
1 parent 43545f3 commit 61fc754

File tree

1 file changed

+21
-8
lines changed

1 file changed

+21
-8
lines changed

src/inspector_agent.cc

+21-8
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ static uv_async_t start_io_thread_async;
6060
// This is just an additional check to make sure start_io_thread_async
6161
// is not accidentally re-used or used when uninitialized.
6262
static std::atomic_bool start_io_thread_async_initialized { false };
63+
// Protects the Agent* stored in start_io_thread_async.data.
64+
static Mutex start_io_thread_async_mutex;
6365

6466
class StartIoTask : public Task {
6567
public:
@@ -97,6 +99,8 @@ static void StartIoThreadWakeup(int signo) {
9799
inline void* StartIoThreadMain(void* unused) {
98100
for (;;) {
99101
uv_sem_wait(&start_io_thread_semaphore);
102+
Mutex::ScopedLock lock(start_io_thread_async_mutex);
103+
100104
CHECK(start_io_thread_async_initialized);
101105
Agent* agent = static_cast<Agent*>(start_io_thread_async.data);
102106
if (agent != nullptr)
@@ -157,6 +161,7 @@ static int StartDebugSignalHandler() {
157161

158162
#ifdef _WIN32
159163
DWORD WINAPI StartIoThreadProc(void* arg) {
164+
Mutex::ScopedLock lock(start_io_thread_async_mutex);
160165
CHECK(start_io_thread_async_initialized);
161166
Agent* agent = static_cast<Agent*>(start_io_thread_async.data);
162167
if (agent != nullptr)
@@ -748,14 +753,7 @@ Agent::Agent(Environment* env)
748753
debug_options_(env->options()->debug_options()),
749754
host_port_(env->inspector_host_port()) {}
750755

751-
Agent::~Agent() {
752-
if (start_io_thread_async.data == this) {
753-
CHECK(start_io_thread_async_initialized.exchange(false));
754-
start_io_thread_async.data = nullptr;
755-
// This is global, will never get freed
756-
uv_close(reinterpret_cast<uv_handle_t*>(&start_io_thread_async), nullptr);
757-
}
758-
}
756+
Agent::~Agent() {}
759757

760758
bool Agent::Start(const std::string& path,
761759
const DebugOptions& options,
@@ -768,6 +766,7 @@ bool Agent::Start(const std::string& path,
768766

769767
client_ = std::make_shared<NodeInspectorClient>(parent_env_, is_main);
770768
if (parent_env_->owns_inspector()) {
769+
Mutex::ScopedLock lock(start_io_thread_async_mutex);
771770
CHECK_EQ(start_io_thread_async_initialized.exchange(true), false);
772771
CHECK_EQ(0, uv_async_init(parent_env_->event_loop(),
773772
&start_io_thread_async,
@@ -776,6 +775,20 @@ bool Agent::Start(const std::string& path,
776775
start_io_thread_async.data = this;
777776
// Ignore failure, SIGUSR1 won't work, but that should not block node start.
778777
StartDebugSignalHandler();
778+
779+
parent_env_->AddCleanupHook([](void* data) {
780+
Environment* env = static_cast<Environment*>(data);
781+
782+
{
783+
Mutex::ScopedLock lock(start_io_thread_async_mutex);
784+
start_io_thread_async.data = nullptr;
785+
}
786+
787+
// This is global, will never get freed
788+
env->CloseHandle(&start_io_thread_async, [](uv_async_t*) {
789+
CHECK(start_io_thread_async_initialized.exchange(false));
790+
});
791+
}, parent_env_);
779792
}
780793

781794
AtExit(parent_env_, [](void* env) {

0 commit comments

Comments
 (0)