-
Notifications
You must be signed in to change notification settings - Fork 29.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
events: remove the abort listener on iterator completion #51091
events: remove the abort listener on iterator completion #51091
Conversation
Where are you actually removing the listener? |
Hi, thanks for taking a look, I changed the signal abort listener to be added using |
Hey, @benjamingr anything else I can help with to move this along? Thank you in advance! |
5bec822
to
430c88e
Compare
430c88e
to
b81974d
Compare
Hey @benjamingr sorry for the bump, I appreciate any time you can spend on this with me, thanks in advance. 🙂 I figure it may help to provide another reason for my motivation behind fixing this issue. I attempted working around the leaked listener by recreating a new controller and signal after the iterator was closed. However, it appeared that creating AbortSignals had a surprising performance cost that was preferable to avoid. Code snippet measuring creating AbortSignalsimport perf_hooks from 'node:perf_hooks'
import util from 'node:util'
import os from 'node:os'
import process from 'node:process'
function abortListener() { }
const ITERATIONS = 1000000;
function noop() { }
let shared = null;
function sharedSignal() {
shared ??= new AbortController().signal;
shared.addEventListener('abort', abortListener);
shared.removeEventListener('abort', abortListener);
}
function recreateSignal() {
const { signal } = new AbortController();
signal.addEventListener('abort', abortListener);
signal.removeEventListener('abort', abortListener);
}
const timerFunctions = [noop, sharedSignal, recreateSignal]
.map(fn => ({ name: fn.name, fn, histogram: perf_hooks.createHistogram() }));
// Warm up
for (const { fn } of timerFunctions) {
for (let i = 0; i < ITERATIONS; i++) {
fn();
}
}
for (const { fn, histogram } of timerFunctions) {
for (let i = 0; i < ITERATIONS; i++) {
histogram.recordDelta();
fn();
histogram.recordDelta();
}
}
console.log('ITERATIONS:', ITERATIONS)
console.log('System:', os.platform(), os.arch(), os.cpus().length + ' cores', 'Node.js ' + process.version)
for (const { name, histogram } of timerFunctions) {
console.log('-'.repeat(20), name, '-'.repeat(20))
const { mean, stddev } = histogram;
const median = histogram.percentiles.get(50)
console.log(util.inspect({ mean, median, stddev }, { breakLength: Infinity, colors: true }))
} The following numbers are in nanoseconds as measured by And for reference an empty function will execute ~15,800 times. ITERATIONS: 1000000
System: win32 x64 16 cores Node.js v20.10.0
-------------------- noop --------------------
{ mean: 65.52607426303713, median: 100, stddev: 165.59818354797054 }
-------------------- sharedSignal --------------------
{ mean: 405.4852127426064, median: 500, stddev: 3071.621721322665 }
-------------------- recreateSignal --------------------
{ mean: 3475.6129638064817, median: 5400, stddev: 21840.742869666145 } |
aeff3ff
to
883b575
Compare
883b575
to
6748f0c
Compare
6748f0c
to
e75733b
Compare
the `abortHandler` function is declared within the scope of the `events.on` function so cannot be removed by the caller which can lead to a memory leak adding the abort listener using the `addAbortListener` helper returns a disposable that can be used to clean up the listener when the iterator is exited Fixes: nodejs#51010
01aade6
to
719f745
Compare
I fixed the commit message line length lint in rebase |
Landed in 40ef2da |
the `abortHandler` function is declared within the scope of the `events.on` function so cannot be removed by the caller which can lead to a memory leak adding the abort listener using the `addAbortListener` helper returns a disposable that can be used to clean up the listener when the iterator is exited Fixes: nodejs#51010 PR-URL: nodejs#51091 Reviewed-By: Chemi Atlow <[email protected]> Reviewed-By: Benjamin Gruenbaum <[email protected]>
the `abortHandler` function is declared within the scope of the `events.on` function so cannot be removed by the caller which can lead to a memory leak adding the abort listener using the `addAbortListener` helper returns a disposable that can be used to clean up the listener when the iterator is exited Fixes: #51010 PR-URL: #51091 Reviewed-By: Chemi Atlow <[email protected]> Reviewed-By: Benjamin Gruenbaum <[email protected]>
the `abortHandler` function is declared within the scope of the `events.on` function so cannot be removed by the caller which can lead to a memory leak adding the abort listener using the `addAbortListener` helper returns a disposable that can be used to clean up the listener when the iterator is exited Fixes: #51010 PR-URL: #51091 Reviewed-By: Chemi Atlow <[email protected]> Reviewed-By: Benjamin Gruenbaum <[email protected]>
the `abortHandler` function is declared within the scope of the `events.on` function so cannot be removed by the caller which can lead to a memory leak adding the abort listener using the `addAbortListener` helper returns a disposable that can be used to clean up the listener when the iterator is exited Fixes: nodejs#51010 PR-URL: nodejs#51091 Reviewed-By: Chemi Atlow <[email protected]> Reviewed-By: Benjamin Gruenbaum <[email protected]>
events: remove abort listener from signal in
on
the
abortHandler
function is declared within the scope ofthe
events.on
function so cannot be removed by the callerwhich can lead to a memory leak
adding the abort listener using the
addAbortListener
helperreturns a disposable that can be used to clean up the listener
when the iterator is exited
Fixes: #51010