Skip to content

inspector: fix abort when two Environments own the inspector - #65877

Open
codebytere wants to merge 2 commits into
nodejs:mainfrom
codebytere:fix/embedder-inspector-sigusr1-per-agent
Open

inspector: fix abort when two Environments own the inspector#65877
codebytere wants to merge 2 commits into
nodejs:mainfrom
codebytere:fix/embedder-inspector-sigusr1-per-agent

Conversation

@codebytere

@codebytere codebytere commented Sep 7, 2026

Copy link
Copy Markdown
Member

Two Environments with default flags that are alive at the same time abort the process on a CHECK in Agent::Start() (the doc/api/embedding.md example on two threads, or two CommonEnvironmentSetups), and every default Environment created after the first leaks a detached SignalInspector thread. The SIGUSR1 "start the inspector io thread" state was a single file-level uv_async_t with a uniqueness CHECK, and the watchdog setup ran once per Environment instead of once per process.

Each Agent whose Environment enables the debug signal handler now gets its own async handle and registers in a list that the watchdog thread walks; the watchdog is set up once per process while SIGUSR1 is still unblocked on each Environment's thread, and an Environment created with kNoStartDebugSignalHandler is no longer woken by another Environment's handler. The second commit documents in node.h that default signal handling also resets the calling thread's signal mask.

Tests: EnvironmentTest.MultipleEnvironmentsPerIsolate now creates both Environments with default flags (aborted before); the inspector, debugger and snapshot suites pass and kill -USR1 still opens the inspector.

Refs: #25777
Refs: #44121


Disclosure: the code, tests and this description were written by Claude Code, directed and reviewed by @codebytere.

@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

Review requested:

  • @nodejs/inspector

@codebytere codebytere added the request-ci Add this label to start a Jenkins CI on a PR. Only starts once the PR has an approving review. label Sep 7, 2026
@nodejs-github-bot nodejs-github-bot added c++ Issues and PRs that require attention from people who are familiar with C++. lib / src Issues and PRs involving general changes in the lib/ or src/ directories. needs-ci PRs that need a full CI run. labels Sep 7, 2026
Two Environments with default flags alive at the same time (for
example the embedding.md example run on two threads, or two
`CommonEnvironmentSetup`s) aborted the process: `Agent::Start()` bound
one file-level static `uv_async_t` to the current Environment's loop
for every Environment with `kOwnsInspector`, which `kDefaultFlags`
implies, and CHECKed that nobody else had. Environments created one
after another did not abort, but each ran `StartDebugSignalHandler()`
again, which re-initialized the semaphore the watchdog waits on and
spawned another detached watchdog thread, leaking one thread per
Environment.

Give every Agent that asks for the debug signal handler its own async
handle, keep those Agents in a mutex-protected list that the watchdog
(or the Windows remote thread) walks, and set the watchdog up once per
process while still unblocking SIGUSR1 on each Environment's thread.
The handle is heap-allocated, closed by the cleanup hook or `~Agent()`,
whichever runs first, and freed by its close callback. A SIGUSR1 now
reaches every Environment that asked for the handler, and no longer
starts the inspector of one that passed `kNoStartDebugSignalHandler`.

Refs: nodejs#25777
Signed-off-by: Shelley Vohr <shelley.vohr@gmail.com>
`InitializeOncePerProcess()` without `kNoDefaultSignalHandling` calls
`pthread_sigmask(SIG_SETMASK, ...)` with a set containing only SIGUSR1,
which unblocks every signal the embedder had blocked on the calling
thread. Say so in the flag's documentation.

Refs: nodejs#44121
Signed-off-by: Shelley Vohr <shelley.vohr@gmail.com>
@codebytere
codebytere force-pushed the fix/embedder-inspector-sigusr1-per-agent branch from 83fbd4e to b1848f1 Compare September 7, 2026 12:43
@codecov

codecov Bot commented Sep 7, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 83.72093% with 7 lines in your changes missing coverage. Please review.
✅ Project coverage is 90.18%. Comparing base (6f38736) to head (b1848f1).
⚠️ Report is 13 commits behind head on main.

Files with missing lines Patch % Lines
src/inspector_agent.cc 83.72% 2 Missing and 5 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #65877      +/-   ##
==========================================
- Coverage   92.40%   90.18%   -2.22%     
==========================================
  Files         417      771     +354     
  Lines      188648   264922   +76274     
  Branches    28849    50311   +21462     
==========================================
+ Hits       174320   238929   +64609     
- Misses      13995    16971    +2976     
- Partials      333     9022    +8689     
Files with missing lines Coverage Δ
src/inspector_agent.h 100.00% <ø> (ø)
src/node.h 91.66% <ø> (ø)
src/inspector_agent.cc 82.46% <83.72%> (ø)

... and 480 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

c++ Issues and PRs that require attention from people who are familiar with C++. lib / src Issues and PRs involving general changes in the lib/ or src/ directories. needs-ci PRs that need a full CI run. request-ci Add this label to start a Jenkins CI on a PR. Only starts once the PR has an approving review.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants