Skip to content

timers: do not retain a reference to the async store after firing#53443

Open
mcollina wants to merge 1 commit into
nodejs:mainfrom
mcollina:timers-leak
Open

timers: do not retain a reference to the async store after firing#53443
mcollina wants to merge 1 commit into
nodejs:mainfrom
mcollina:timers-leak

Conversation

@mcollina
Copy link
Copy Markdown
Member

@mcollina mcollina commented Jun 13, 2024

After firing timers, we can clean them up by iterating over all active stores and setting the relevant symbols to undefined.

This is still a draft because we will need to extend it to immediates and intervals, too.

Fixes #53408

@nodejs-github-bot nodejs-github-bot added async_hooks Issues and PRs related to the async hooks subsystem. needs-ci PRs that need a full CI run. timers Issues and PRs related to the timers subsystem / setImmediate, setInterval, setTimeout. labels Jun 13, 2024
@mcollina
Copy link
Copy Markdown
Member Author

I've opened a draft PR to discuss a possible solution to #53408.

cc @nodejs/diagnostics @nodejs/timers let me know what you think.

Comment thread lib/internal/timers.js Outdated
node.priorityQueuePosition = pos;
}

function removeAllStores (timer) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about moving this function into async_hooks to avoid the need to expose getActiveStores() there?
An alternative would be to use a single object on the resource which holds all stores. This would reduce the number of properties added on resource objects - and avoid the need of a for loop to clear it.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A helper that receives a resource and clears all the store data off it would be better than exposing the stores list as we do now. As it is presently, one could get and retain access to a store in a way unintentional from the perspective of the store owner, which is a bit dangerous from a memory perspective.

Comment thread lib/internal/timers.js Outdated
timeoutInfo[0]--;

removeAllStores(timer);
timer._onTimeout = undefined;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe clear also _timerArgs.

After firing timers, we can clean them up by iterating over all active
stores and setting the relevant symbols to undefined.

This also covers immediates and intervals.

Refs: nodejs#53408
PR-URL: nodejs#53443
Fixes: nodejs#53408
@mcollina mcollina marked this pull request as ready for review May 31, 2026 19:12
@mcollina
Copy link
Copy Markdown
Member Author

I've updated this PR to address the review feedback:

  1. Instead of exposing getActiveStores(), added a clearStores(resource) helper function in async_hooks that takes a resource and clears all store references from it. This avoids exposing the internal stores list.
  2. Added cleanup for _timerArgs in addition to _onTimeout and async stores.
  3. Extended the cleanup to immediates (setImmediate) and intervals (setInterval) as well.
  4. Updated the test to cover all three cases.

cc @Flarna @Qard

@codecov
Copy link
Copy Markdown

codecov Bot commented May 31, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 90.34%. Comparing base (040c51c) to head (0ad04f6).
⚠️ Report is 7 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main   #53443   +/-   ##
=======================================
  Coverage   90.34%   90.34%           
=======================================
  Files         732      732           
  Lines      236455   236542   +87     
  Branches    44528    44534    +6     
=======================================
+ Hits       213615   213697   +82     
- Misses      14544    14545    +1     
- Partials     8296     8300    +4     
Files with missing lines Coverage Δ
lib/async_hooks.js 100.00% <100.00%> (ø)
lib/internal/async_local_storage/async_hooks.js 98.12% <100.00%> (+0.08%) ⬆️
lib/internal/timers.js 99.86% <100.00%> (+0.14%) ⬆️

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

async_hooks Issues and PRs related to the async hooks subsystem. needs-ci PRs that need a full CI run. timers Issues and PRs related to the timers subsystem / setImmediate, setInterval, setTimeout.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Clear kResourceStore in timers and intervals

4 participants