Skip to content

src: keep per-Environment state out of thread_locals - #66411

Open
codebytere wants to merge 4 commits into
nodejs:mainfrom
codebytere:src-per-env-thread-locals
Open

codebytere wants to merge 4 commits into
nodejs:mainfrom
codebytere:src-per-env-thread-locals

Conversation

@codebytere

Copy link
Copy Markdown
Member

Refs: #66239

Two thread_locals in src/ hold state that belongs to one Environment, so they break when several Environments share a thread:

  • The root cert store: tls.setDefaultCACertificates() in one Environment replaced the trusted CAs of every other Environment on the thread. It now lives on the Environment, next to its other OpenSSL state.
  • The QUIC allocator: its BindingData pointer came from whichever BindingData last handed out an allocator, so a session in one Environment freed memory against another's counter and failed a CHECK. Each BindingData now owns its allocator state. nghttp3 buffers backing external strings can outlive the BindingData, so the state is deleted once the BindingData is gone and the last allocation made through it is freed.

A new cpplint rule rejects thread_local in src/ unless the declaration is marked with NOLINTNEXTLINE(runtime/thread_local). The 11 remaining uses are marked: re-entrancy guards, debug counters, dlopen bookkeeping, the context setup BuiltinLoader and the handle cleanup depth, all per-thread by design.

Both fixes come with a cctest in test_environment_shared_isolate.cc that fails without them.


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

The root cert store and the certificates set through
tls.setDefaultCACertificates() were thread_local, with a cleanup hook on
whichever Environment used TLS first. When several Environments share a
thread, setting the default CA certificates in one of them replaced the
trusted CAs of the others. Keep both on the Environment, next to its
other OpenSSL state.

Signed-off-by: Shelley Vohr <shelley.vohr@gmail.com>
The ngtcp2 and nghttp3 allocators shared one thread_local state whose
BindingData pointer was set by the last BindingData that handed out an
allocator. With several Environments on a thread, memory allocated for
a session in one Environment was accounted against another's
BindingData and failed a CHECK when freed.

Give each BindingData its own heap-allocated state. nghttp3 buffers
backing external strings can be freed after the BindingData is gone,
so the state counts live allocations and is deleted once the
BindingData has been destroyed and the last of them is freed.

Signed-off-by: Shelley Vohr <shelley.vohr@gmail.com>
Several Environments can share a thread, so state in src/ that belongs
to one of them cannot be kept in a thread_local. Add a cpplint rule
that rejects thread_local in src/ unless the declaration is marked with
NOLINTNEXTLINE(runtime/thread_local), and mark the existing uses, which
are per-thread by design.

Signed-off-by: Shelley Vohr <shelley.vohr@gmail.com>
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

Review requested:

  • @nodejs/crypto
  • @nodejs/quic

@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 30, 2026
@codecov

codecov Bot commented Sep 30, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 91.30435% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 90.37%. Comparing base (8bf7793) to head (94d5ce4).
⚠️ Report is 7 commits behind head on main.

Files with missing lines Patch % Lines
src/crypto/crypto_context.cc 91.30% 0 Missing and 2 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #66411      +/-   ##
==========================================
- Coverage   90.39%   90.37%   -0.02%     
==========================================
  Files         792      792              
  Lines      275580   275574       -6     
  Branches    52840    52839       -1     
==========================================
- Hits       249104   249058      -46     
- Misses      16897    16943      +46     
+ Partials     9579     9573       -6     
Files with missing lines Coverage Δ
src/api/environment.cc 81.15% <ø> (+1.33%) ⬆️
src/env.cc 82.00% <ø> (-0.27%) ⬇️
src/env.h 97.40% <ø> (ø)
src/node_binding.cc 70.79% <ø> (ø)
src/node_errors.cc 62.36% <ø> (-0.14%) ⬇️
src/node_internals.h 80.35% <ø> (ø)
src/crypto/crypto_context.cc 71.49% <91.30%> (-0.22%) ⬇️

... and 29 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.

Comment thread src/env.cc Outdated
@addaleax addaleax 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 30, 2026
Signed-off-by: Shelley Vohr <shelley.vohr@gmail.com>
@github-actions github-actions Bot removed 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 30, 2026
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

@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 30, 2026
@github-actions github-actions Bot added request-ci-failed Starting CI with the request-ci label failed and requires manual intervention. and removed request-ci Add this label to start a Jenkins CI on a PR. Only starts once the PR has an approving review. labels Sep 30, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Failed to start CI

   ℹ  Existing CI run found: https://ci.nodejs.org/job/node-test-pull-request/78102/
   ✖  Refusing to start a potentially duplicate CI job. The existing CI run is still in progress.
Full Auto Start CI output
�[36m⠋�[39m Getting reviews from nodejs/node/pull/66411
�[36m⠋�[39m Getting commits from nodejs/node/pull/66411
�[36m⠙�[39m Validating Jenkins credentials
�[36m⠙�[39m Validating Jenkins credentials
✔  Jenkins credentials valid
�[36m⠹�[39m Getting comments from nodejs/node/pull/66411
�[36m⠸�[39m Querying data for job/node-test-pull-request/78102/
�[36m⠸�[39m Querying data for job/node-test-pull-request/78102/
�[36m⠸�[39m Querying API for job/node-test-pull-request/78102/
✔  Build data downloaded
   ℹ  Existing CI run found: https://ci.nodejs.org/job/node-test-pull-request/78102/
   ✖  Refusing to start a potentially duplicate CI job. The existing CI run is still in progress.

View workflow run

@panva panva removed the request-ci-failed Starting CI with the request-ci label failed and requires manual intervention. label Sep 30, 2026
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

@panva panva added the author ready PRs with CI started, the required approvals, and no outstanding review comments. label Sep 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

author ready PRs with CI started, the required approvals, and no outstanding review comments. 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.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants