-
Notifications
You must be signed in to change notification settings - Fork 30k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
timers: don't mutate unref list while iterating it
Commit 934bfe2 had introduced a regression where node would crash trying to access a null unref timer if a given unref timer's callback would remove other unref timers set to fire in the future. More generally, it makes the unrefTimeout function more solid by not mutating the unrefList while traversing it. Fixes: nodejs/node-v0.x-archive#8897 Conflicts: lib/timers.js Fixes: nodejs/node-convergence-archive#23 Ref: #268 PR-URL: #2540 Reviewed-By: bnoordhuis - Ben Noordhuis <info@bnoordhuis.nl>
- Loading branch information
1 parent
7a8c3e0
commit 403d7ee
Showing
4 changed files
with
169 additions
and
36 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
50 changes: 50 additions & 0 deletions
50
test/parallel/test-timers-socket-timeout-removes-other-socket-unref-timer.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
'use strict'; | ||
|
||
/* | ||
* This test is a regression test for joyent/node#8897. | ||
*/ | ||
|
||
const common = require('../common'); | ||
const assert = require('assert'); | ||
const net = require('net'); | ||
|
||
const clients = []; | ||
|
||
const server = net.createServer(function onClient(client) { | ||
clients.push(client); | ||
|
||
if (clients.length === 2) { | ||
/* | ||
* Enroll two timers, and make the one supposed to fire first | ||
* unenroll the other one supposed to fire later. This mutates | ||
* the list of unref timers when traversing it, and exposes the | ||
* original issue in joyent/node#8897. | ||
*/ | ||
clients[0].setTimeout(1, function onTimeout() { | ||
clients[1].setTimeout(0); | ||
clients[0].end(); | ||
clients[1].end(); | ||
}); | ||
|
||
// Use a delay that is higher than the lowest timer resolution accross all | ||
// supported platforms, so that the two timers don't fire at the same time. | ||
clients[1].setTimeout(50); | ||
} | ||
}); | ||
|
||
server.listen(common.PORT, common.localhostIPv4, function() { | ||
var nbClientsEnded = 0; | ||
|
||
function addEndedClient(client) { | ||
++nbClientsEnded; | ||
if (nbClientsEnded === 2) { | ||
server.close(); | ||
} | ||
}; | ||
|
||
const client1 = net.connect({ port: common.PORT }); | ||
client1.on('end', addEndedClient); | ||
|
||
const client2 = net.connect({ port: common.PORT }); | ||
client2.on('end', addEndedClient); | ||
}); |
41 changes: 41 additions & 0 deletions
41
test/parallel/test-timers-unref-remove-other-unref-timers-only-one-fires.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
'use strict'; | ||
|
||
/* | ||
* The goal of this test is to make sure that, after the regression introduced | ||
* by 934bfe23a16556d05bfb1844ef4d53e8c9887c3d, the fix preserves the following | ||
* behavior of unref timers: if two timers are scheduled to fire at the same | ||
* time, if one unenrolls the other one in its _onTimeout callback, the other | ||
* one will *not* fire. | ||
* | ||
* This behavior is a private implementation detail and should not be | ||
* considered public interface. | ||
*/ | ||
const common = require('../common'); | ||
const timers = require('timers'); | ||
const assert = require('assert'); | ||
|
||
var nbTimersFired = 0; | ||
|
||
const foo = { | ||
_onTimeout: function() { | ||
++nbTimersFired; | ||
timers.unenroll(bar); | ||
} | ||
}; | ||
|
||
const bar = { | ||
_onTimeout: function() { | ||
++nbTimersFired; | ||
timers.unenroll(foo); | ||
} | ||
}; | ||
|
||
timers.enroll(bar, 1); | ||
timers._unrefActive(bar); | ||
|
||
timers.enroll(foo, 1); | ||
timers._unrefActive(foo); | ||
|
||
setTimeout(function() { | ||
assert.notEqual(nbTimersFired, 2); | ||
}, 20); |
33 changes: 33 additions & 0 deletions
33
test/parallel/test-timers-unref-remove-other-unref-timers.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
'use strict'; | ||
|
||
/* | ||
* This test is a regression test for joyent/node#8897. | ||
* | ||
* It tests some private implementation details that should not be | ||
* considered public interface. | ||
*/ | ||
const common = require('../common'); | ||
const assert = require('assert'); | ||
const timers = require('timers'); | ||
|
||
const foo = { | ||
_onTimeout: assert.fail | ||
}; | ||
|
||
const bar = { | ||
_onTimeout: common.mustCall(function() { | ||
timers.unenroll(foo); | ||
}) | ||
}; | ||
|
||
// We use timers with expiration times that are sufficiently apart to make | ||
// sure that they're not fired at the same time on platforms where the timer | ||
// resolution is a bit coarse (e.g Windows with a default resolution of ~15ms). | ||
timers.enroll(bar, 1); | ||
timers._unrefActive(bar); | ||
|
||
timers.enroll(foo, 50); | ||
timers._unrefActive(foo); | ||
|
||
// Keep the process open. | ||
setTimeout(function() {}, 100); |