Skip to content

Commit 66eb7f4

Browse files
authored
fix(backport): memory leaks due to timer references outliving the timers (#2773) (#2773)
fix: memory leaks due to timer references outliving the timers (#2773) * fix: remove unused setInterval spy * fix: delete timer references after callback This way we don't hold any strong reference to potentially large setTimeout/setImmediate contexts any more. Since we use neither `clearTimeout`, nor `clearImmediate`, we can just ignore those cases and don't need to install spies to handle deliberate clears.
1 parent e92cdfa commit 66eb7f4

2 files changed

Lines changed: 15 additions & 15 deletions

File tree

lib/common.js

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -593,31 +593,35 @@ function deepEqual(expected, actual) {
593593
return expected === actual
594594
}
595595

596-
const timeouts = []
597-
const intervals = []
598-
const immediates = []
596+
const timeouts = new Set()
597+
const immediates = new Set()
599598

600599
const wrapTimer =
601600
(timer, ids) =>
602-
(...args) => {
603-
const id = timer(...args)
604-
ids.push(id)
601+
(callback, ...timerArgs) => {
602+
const cb = (...callbackArgs) => {
603+
try {
604+
// eslint-disable-next-line n/no-callback-literal
605+
callback(...callbackArgs)
606+
} finally {
607+
ids.delete(id)
608+
}
609+
}
610+
const id = timer(cb, ...timerArgs)
611+
ids.add(id)
605612
return id
606613
}
607614

608615
const setTimeout = wrapTimer(timers.setTimeout, timeouts)
609-
const setInterval = wrapTimer(timers.setInterval, intervals)
610616
const setImmediate = wrapTimer(timers.setImmediate, immediates)
611617

612618
function clearTimer(clear, ids) {
613-
while (ids.length) {
614-
clear(ids.shift())
615-
}
619+
ids.forEach(clear)
620+
ids.clear()
616621
}
617622

618623
function removeAllTimers() {
619624
clearTimer(clearTimeout, timeouts)
620-
clearTimer(clearInterval, intervals)
621625
clearTimer(clearImmediate, immediates)
622626
}
623627

@@ -762,7 +766,6 @@ module.exports = {
762766
removeAllTimers,
763767
restoreOverriddenRequests,
764768
setImmediate,
765-
setInterval,
766769
setTimeout,
767770
stringifyRequest,
768771
}

tests/test_common.js

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -530,17 +530,14 @@ describe('`dataEqual()`', () => {
530530

531531
it('testing timers are deleted correctly', done => {
532532
const timeoutSpy = sinon.spy()
533-
const intervalSpy = sinon.spy()
534533
const immediateSpy = sinon.spy()
535534

536535
common.setTimeout(timeoutSpy, 0)
537-
common.setInterval(intervalSpy, 0)
538536
common.setImmediate(immediateSpy)
539537
common.removeAllTimers()
540538

541539
setImmediate(() => {
542540
expect(timeoutSpy).to.not.have.been.called()
543-
expect(intervalSpy).to.not.have.been.called()
544541
expect(immediateSpy).to.not.have.been.called()
545542
done()
546543
})

0 commit comments

Comments
 (0)