@@ -70,19 +70,18 @@ using v8_inspector::V8InspectorClient;
7070#ifdef __POSIX__
7171static uv_sem_t start_io_thread_semaphore;
7272#endif // __POSIX__
73- static uv_async_t start_io_thread_async;
74- // This is just an additional check to make sure start_io_thread_async
75- // is not accidentally re-used or used when uninitialized .
76- static std::atomic_bool start_io_thread_async_initialized { false } ;
77- // Protects the Agent* stored in start_io_thread_async.data.
78- static Mutex start_io_thread_async_mutex ;
79-
80- // Called on the main thread.
81- void StartIoThreadAsyncCallback ( uv_async_t * handle) {
82- static_cast < Agent*>(handle-> data )-> StartIoThread ();
73+ // Agents that asked for the debug signal handler; SIGUSR1 (or the Windows
74+ // remote thread) starts the io thread of each. The mutex also guards the
75+ // once-per-process watchdog setup .
76+ static Mutex start_io_thread_agents_mutex ;
77+ static std::vector< Agent*> start_io_thread_agents;
78+ static bool debug_signal_handler_started = false ;
79+
80+ static void RequestIoThreadStartOnAgents () {
81+ Mutex::ScopedLock lock (start_io_thread_agents_mutex);
82+ for ( Agent* agent : start_io_thread_agents) agent-> RequestIoThreadStart ();
8383}
8484
85-
8685#ifdef __POSIX__
8786static void StartIoThreadWakeup (int signo, siginfo_t * info, void * ucontext) {
8887 uv_sem_post (&start_io_thread_semaphore);
@@ -92,16 +91,11 @@ inline void* StartIoThreadMain(void* unused) {
9291 uv_thread_setname (" SignalInspector" );
9392 for (;;) {
9493 uv_sem_wait (&start_io_thread_semaphore);
95- Mutex::ScopedLock lock (start_io_thread_async_mutex);
96-
97- CHECK (start_io_thread_async_initialized);
98- Agent* agent = static_cast <Agent*>(start_io_thread_async.data );
99- if (agent != nullptr )
100- agent->RequestIoThreadStart ();
94+ RequestIoThreadStartOnAgents ();
10195 }
10296}
10397
104- static int StartDebugSignalHandler () {
98+ static int StartWatchdogThread () {
10599 // Start a watchdog thread for calling v8::Debug::DebugBreak() because
106100 // it's not safe to call directly from the signal handler, it can
107101 // deadlock with the thread it interrupts.
@@ -136,14 +130,28 @@ static int StartDebugSignalHandler() {
136130 fprintf (stderr, " node[%u]: pthread_create: %s\n " ,
137131 uv_os_getpid (), strerror (err));
138132 fflush (stderr);
139- // Leave SIGUSR1 blocked. We don't install a signal handler,
140- // receiving the signal would terminate the process.
133+ uv_sem_destroy (&start_io_thread_semaphore);
141134 return -err;
142135 }
143136 RegisterSignalHandler (SIGUSR1 , StartIoThreadWakeup);
144137 // Restore original mask
145138 CHECK_EQ (0 , pthread_sigmask (SIG_SETMASK , &sigmask, nullptr ));
146- // Unblock SIGUSR1. A pending SIGUSR1 signal will now be delivered.
139+ return 0 ;
140+ }
141+
142+ static int StartDebugSignalHandler () {
143+ {
144+ Mutex::ScopedLock lock (start_io_thread_agents_mutex);
145+ if (!debug_signal_handler_started) {
146+ // Leave SIGUSR1 blocked on failure. We don't install a signal handler,
147+ // receiving the signal would terminate the process.
148+ if (int err = StartWatchdogThread ()) return err;
149+ debug_signal_handler_started = true ;
150+ }
151+ }
152+ // Unblock SIGUSR1 on this thread; PlatformInit() left it blocked. A pending
153+ // SIGUSR1 signal will now be delivered.
154+ sigset_t sigmask;
147155 sigemptyset (&sigmask);
148156 sigaddset (&sigmask, SIGUSR1 );
149157 CHECK_EQ (0 , pthread_sigmask (SIG_UNBLOCK , &sigmask, nullptr ));
@@ -154,11 +162,7 @@ static int StartDebugSignalHandler() {
154162
155163#ifdef _WIN32
156164DWORD WINAPI StartIoThreadProc (void * arg) {
157- Mutex::ScopedLock lock (start_io_thread_async_mutex);
158- CHECK (start_io_thread_async_initialized);
159- Agent* agent = static_cast <Agent*>(start_io_thread_async.data );
160- if (agent != nullptr )
161- agent->RequestIoThreadStart ();
165+ RequestIoThreadStartOnAgents ();
162166 return 0 ;
163167}
164168
@@ -168,6 +172,9 @@ static int GetDebugSignalHandlerMappingName(DWORD pid, wchar_t* buf,
168172}
169173
170174static int StartDebugSignalHandler () {
175+ Mutex::ScopedLock lock (start_io_thread_agents_mutex);
176+ if (debug_signal_handler_started) return 0 ;
177+ debug_signal_handler_started = true ;
171178 wchar_t mapping_name[32 ];
172179 HANDLE mapping_handle;
173180 DWORD pid;
@@ -837,7 +844,21 @@ Agent::Agent(Environment* env)
837844 debug_options_ (env->options ()->debug_options()),
838845 host_port_(env->inspector_host_port ()) {}
839846
840- Agent::~Agent () = default ;
847+ Agent::~Agent () {
848+ StopAcceptingIoThreadStarts ();
849+ }
850+
851+ void Agent::StopAcceptingIoThreadStarts () {
852+ if (start_io_thread_async_ == nullptr ) return ;
853+ {
854+ Mutex::ScopedLock lock (start_io_thread_agents_mutex);
855+ std::erase (start_io_thread_agents, this );
856+ }
857+ parent_env_->RemoveCleanupHook (StopAcceptingIoThreadStartsHook, this );
858+ parent_env_->CloseHandle (start_io_thread_async_,
859+ [](uv_async_t * handle) { delete handle; });
860+ start_io_thread_async_ = nullptr ;
861+ }
841862
842863bool Agent::Start (const std::string& path,
843864 const DebugOptions& options,
@@ -849,33 +870,25 @@ bool Agent::Start(const std::string& path,
849870 host_port_ = host_port;
850871
851872 client_ = std::make_shared<NodeInspectorClient>(parent_env_, is_main);
852- if (parent_env_->owns_inspector ()) {
853- Mutex::ScopedLock lock (start_io_thread_async_mutex);
854- CHECK_EQ (start_io_thread_async_initialized.exchange (true ), false );
855- CHECK_EQ (0 , uv_async_init (parent_env_->event_loop (),
856- &start_io_thread_async,
857- StartIoThreadAsyncCallback));
858- uv_unref (reinterpret_cast <uv_handle_t *>(&start_io_thread_async));
859- start_io_thread_async.data = this ;
860- if (parent_env_->should_start_debug_signal_handler ()) {
861- // Ignore failure, SIGUSR1 won't work, but that should not block node
862- // start.
863- StartDebugSignalHandler ();
873+ if (parent_env_->owns_inspector () &&
874+ parent_env_->should_start_debug_signal_handler ()) {
875+ start_io_thread_async_ = new uv_async_t ;
876+ start_io_thread_async_->data = this ;
877+ CHECK_EQ (0 ,
878+ uv_async_init (parent_env_->event_loop (),
879+ start_io_thread_async_,
880+ [](uv_async_t * handle) {
881+ static_cast <Agent*>(handle->data )->StartIoThread ();
882+ }));
883+ uv_unref (reinterpret_cast <uv_handle_t *>(start_io_thread_async_));
884+ {
885+ Mutex::ScopedLock lock (start_io_thread_agents_mutex);
886+ start_io_thread_agents.push_back (this );
864887 }
865-
866- parent_env_->AddCleanupHook ([](void * data) {
867- Environment* env = static_cast <Environment*>(data);
868-
869- {
870- Mutex::ScopedLock lock (start_io_thread_async_mutex);
871- start_io_thread_async.data = nullptr ;
872- }
873-
874- // This is global, will never get freed
875- env->CloseHandle (&start_io_thread_async, [](uv_async_t *) {
876- CHECK (start_io_thread_async_initialized.exchange (false ));
877- });
878- }, parent_env_);
888+ parent_env_->AddCleanupHook (StopAcceptingIoThreadStartsHook, this );
889+ // Ignore failure, SIGUSR1 won't work, but that should not block node
890+ // start.
891+ StartDebugSignalHandler ();
879892 }
880893
881894 AtExit (parent_env_, [](void * env) {
@@ -1154,21 +1167,21 @@ void Agent::AllAsyncTasksCanceled() {
11541167 client_->AllAsyncTasksCanceled ();
11551168}
11561169
1170+ void Agent::StopAcceptingIoThreadStartsHook (void * agent) {
1171+ static_cast <Agent*>(agent)->StopAcceptingIoThreadStarts ();
1172+ }
1173+
11571174void Agent::RequestIoThreadStart () {
11581175 // We need to attempt to interrupt V8 flow (in case Node is running
11591176 // continuous JS code) and to wake up libuv thread (in case Node is waiting
11601177 // for IO events)
11611178 if (!options ().allow_attaching_debugger ) {
11621179 return ;
11631180 }
1164- CHECK (start_io_thread_async_initialized);
1165- uv_async_send (&start_io_thread_async);
11661181 parent_env_->RequestInterrupt ([this ](Environment*) {
11671182 StartIoThread ();
11681183 });
1169-
1170- CHECK (start_io_thread_async_initialized);
1171- uv_async_send (&start_io_thread_async);
1184+ uv_async_send (start_io_thread_async_);
11721185}
11731186
11741187void Agent::ContextCreated (Local<Context> context, const ContextInfo& info) {
0 commit comments