diff --git a/lib/internal/timers.js b/lib/internal/timers.js index 30208253d60..96a0b5b7f7a 100644 --- a/lib/internal/timers.js +++ b/lib/internal/timers.js @@ -633,10 +633,14 @@ function getTimerCallbacks(runNextTicks) { return; } - if (ranAtLeastOneTimer) + if (ranAtLeastOneTimer) { runNextTicks(); - else + // The ticks may have cleared or rescheduled this timer. + if (L.peek(list) !== timer || now - timer._idleStart < msecs) + continue; + } else { ranAtLeastOneTimer = true; + } // The actual logic for when a timeout happens. L.remove(timer); diff --git a/test/parallel/test-net-socket-timeout-rearm-in-next-tick.js b/test/parallel/test-net-socket-timeout-rearm-in-next-tick.js new file mode 100644 index 00000000000..64dcd8257c0 --- /dev/null +++ b/test/parallel/test-net-socket-timeout-rearm-in-next-tick.js @@ -0,0 +1,28 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); + +// Re-arming a socket timeout from a nextTick queued by a timer that runs just +// before it in the same timer list must replace the old timeout. + +const server = net.createServer().listen(0, common.mustCall(() => { + const socket = net.connect(server.address().port, common.mustCall(() => { + let rearmed = false; + + setTimeout(common.mustCall(() => { + process.nextTick(() => { + socket.setTimeout(500); + setImmediate(() => { rearmed = true; }); + }); + }), 300); + socket.setTimeout(300); + + socket.on('timeout', common.mustCall(() => { + assert.ok(rearmed); + socket.destroy(); + server.close(); + })); + })); +}));