Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
From ca49899284f701be6f672ec2c9e55c5a81ad9007 Mon Sep 17 00:00:00 2001
From: isaacs <i@izs.me>
Date: Thu, 31 Oct 2013 12:15:14 -0700
Subject: [PATCH] timers: handle errors throw in setImmediate
Use the same `try/finally` trick that we do for setTimeout throws.
Tangentially related to #GH-6011, root cause of error identified in
https://github.com/joyent/node/pull/6011/#issuecomment-27452646
---
lib/timers.js | 18 +-
test/simple/test-asynclistener-multi-timeout.js | 376 ++++++++++++++++++++++++
2 files changed, 393 insertions(+), 1 deletion(-)
create mode 100644 test/simple/test-asynclistener-multi-timeout.js
diff --git a/lib/timers.js b/lib/timers.js
index c00f183..efca41a 100644
--- a/lib/timers.js
+++ b/lib/timers.js
@@ -378,7 +378,23 @@ function processImmediate() {
hasQueue = !!immediate._asyncQueue;
if (hasQueue)
loadAsyncQueue(immediate);
- immediate._onImmediate();
+ var threw = true;
+ try {
+ immediate._onImmediate();
+ threw = false;
+ } finally {
+ if (threw) {
+ if (!L.isEmpty(queue)) {
+ // Handle any remaining on next tick, assuming we're still
+ // alive to do so.
+ while (!L.isEmpty(immediateQueue)) {
+ L.append(queue, L.shift(immediateQueue));
+ }
+ immediateQueue = queue;
+ process.nextTick(processImmediate);
+ }
+ }
+ }
if (hasQueue)
unloadAsyncQueue(immediate);
}
diff --git a/test/simple/test-asynclistener-multi-timeout.js b/test/simple/test-asynclistener-multi-timeout.js
new file mode 100644
index 0000000..77d5d5c
--- /dev/null
+++ b/test/simple/test-asynclistener-multi-timeout.js
@@ -0,0 +1,376 @@
+// Copyright Joyent, Inc. and other Node contributors.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included
+// in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+var common = require('../common');
+var assert = require('assert');
+
+var addListener = process.addAsyncListener;
+var removeListener = process.removeAsyncListener;
+
+// test adding and throwing with many timeouts in play
+// on the same timer msecs value
+
+var caught = [];
+var expect = [];
+
+function asyncL(a) {}
+
+var callbacksObj = {
+ error: function(value, er) {
+ console.error('caught', er.message);
+ caught.push(er.message);
+ return (expect.indexOf(er.message) !== -1);
+ }
+};
+
+var listener = process.createAsyncListener(asyncL, callbacksObj);
+
+process.on('exit', function(code) {
+ removeListener(listener);
+
+ if (code > 0)
+ return;
+
+ expect = expect.sort();
+ caught = caught.sort();
+
+ console.error('missed', expect.filter(function(f) {
+ return caught.indexOf(f) === -1;
+ }));
+ assert.deepEqual(caught, expect, 'caught all expected errors');
+ console.log('ok');
+});
+
+expect.push('sync throw');
+process.nextTick(function() {
+ addListener(listener);
+ throw new Error('sync throw');
+ removeListener(listener);
+});
+
+expect.push('sync throw 2');
+addListener(listener);
+process.nextTick(function() {
+ throw new Error('sync throw 2');
+});
+removeListener(listener);
+
+expect.push('one timer');
+process.nextTick(function() {
+ addListener(listener);
+ setTimeout(function() {
+ throw new Error('one timer');
+ });
+ removeListener(listener);
+});
+
+expect.push('one timer 2');
+addListener(listener);
+process.nextTick(function() {
+ setTimeout(function() {
+ throw new Error('one timer 2');
+ });
+});
+removeListener(listener);
+
+expect.push('nest timers');
+process.nextTick(function() {
+ addListener(listener);
+ setTimeout(function() {
+ setTimeout(function() {
+ throw new Error('nest timers');
+ });
+ });
+ removeListener(listener);
+});
+
+expect.push('nest timers 2');
+addListener(listener);
+process.nextTick(function() {
+ setTimeout(function() {
+ setTimeout(function() {
+ throw new Error('nest timers 2');
+ });
+ });
+});
+removeListener(listener);
+
+expect.push('two timers a');
+expect.push('two timers b');
+process.nextTick(function() {
+ addListener(listener);
+ setTimeout(function() {
+ throw new Error('two timers a');
+ });
+ setTimeout(function() {
+ throw new Error('two timers b');
+ });
+ removeListener(listener);
+});
+
+expect.push('two timers a 2');
+expect.push('two timers b 2');
+addListener(listener);
+process.nextTick(function() {
+ setTimeout(function() {
+ throw new Error('two timers a 2');
+ });
+ setTimeout(function() {
+ throw new Error('two timers b 2');
+ });
+});
+removeListener(listener);
+
+expect.push('two nested timers a a');
+expect.push('two nested timers a b');
+expect.push('two nested timers a');
+expect.push('two nested timers b a');
+expect.push('two nested timers b b');
+expect.push('two nested timers b');
+expect.push('two nested timers');
+process.nextTick(function() {
+ addListener(listener);
+ setTimeout(function() {
+ setTimeout(function() {
+ throw new Error('two nested timers a a');
+ });
+ setTimeout(function() {
+ throw new Error('two nested timers a b');
+ });
+ throw new Error('two nested timers a');
+ });
+ setTimeout(function() {
+ setTimeout(function() {
+ throw new Error('two nested timers b a');
+ });
+ setTimeout(function() {
+ throw new Error('two nested timers b b');
+ });
+ throw new Error('two nested timers b');
+ });
+ throw new Error('two nested timers');
+ removeListener(listener);
+});
+
+expect.push('two nested timers a a 2');
+expect.push('two nested timers a b 2');
+expect.push('two nested timers a 2');
+expect.push('two nested timers b a 2');
+expect.push('two nested timers b b 2');
+expect.push('two nested timers b 2');
+expect.push('two nested timers 2');
+addListener(listener);
+process.nextTick(function() {
+ setTimeout(function() {
+ setTimeout(function() {
+ throw new Error('two nested timers a a 2');
+ });
+ setTimeout(function() {
+ throw new Error('two nested timers a b 2');
+ });
+ throw new Error('two nested timers a 2');
+ });
+ setTimeout(function() {
+ setTimeout(function() {
+ throw new Error('two nested timers b a 2');
+ });
+ setTimeout(function() {
+ throw new Error('two nested timers b b 2');
+ });
+ throw new Error('two nested timers b 2');
+ });
+ throw new Error('two nested timers 2');
+});
+removeListener(listener);
+
+expect.push('immediate simple');
+process.nextTick(function() {
+ addListener(listener);
+ setImmediate(function() {
+ throw new Error('immediate simple');
+ });
+ removeListener(listener);
+});
+
+expect.push('immediate simple 2');
+addListener(listener);
+process.nextTick(function() {
+ setImmediate(function() {
+ throw new Error('immediate simple 2');
+ });
+});
+removeListener(listener);
+
+expect.push('two immediate a');
+expect.push('two immediate b');
+process.nextTick(function() {
+ addListener(listener);
+ setImmediate(function() {
+ throw new Error('two immediate a');
+ });
+ setImmediate(function() {
+ throw new Error('two immediate b');
+ });
+ removeListener(listener);
+});
+
+expect.push('two immediate a 2');
+expect.push('two immediate b 2');
+addListener(listener);
+process.nextTick(function() {
+ setImmediate(function() {
+ throw new Error('two immediate a 2');
+ });
+ setImmediate(function() {
+ throw new Error('two immediate b 2');
+ });
+});
+removeListener(listener);
+
+expect.push('nest immediate child');
+expect.push('nest immediate parent');
+process.nextTick(function() {
+ addListener(listener);
+ setImmediate(function() {
+ setImmediate(function() {
+ throw new Error('nest immediate child');
+ });
+ throw new Error('nest immediate parent');
+ });
+ removeListener(listener);
+});
+
+
+expect.push('nest immediate child 2');
+expect.push('nest immediate parent 2');
+addListener(listener);
+process.nextTick(function() {
+ setImmediate(function() {
+ setImmediate(function() {
+ throw new Error('nest immediate child 2');
+ });
+ throw new Error('nest immediate parent 2');
+ });
+});
+removeListener(listener);
+
+
+expect.push('mix immediate timeout');
+process.nextTick(function() {
+ addListener(listener);
+ setTimeout(function() {
+ setImmediate(function() {
+ throw new Error('mix immediate timeout');
+ });
+ });
+ removeListener(listener);
+});
+
+expect.push('mix immediate timeout 2');
+addListener(listener);
+process.nextTick(function() {
+ setTimeout(function() {
+ setImmediate(function() {
+ throw new Error('mix immediate timeout 2');
+ });
+ });
+});
+removeListener(listener);
+
+expect.push('interval simple');
+process.nextTick(function() {
+ addListener(listener);
+ var i = setInterval(function() {
+ clearInterval(i);
+ throw new Error('interval simple');
+ });
+ removeListener(listener);
+});
+
+expect.push('interval simple 2');
+addListener(listener);
+process.nextTick(function() {
+ var i = setInterval(function() {
+ clearInterval(i);
+ throw new Error('interval simple 2');
+ });
+});
+removeListener(listener);
+
+expect.push('two interval a');
+expect.push('two interval b');
+process.nextTick(function() {
+ addListener(listener);
+ var i = setInterval(function() {
+ clearInterval(i);
+ throw new Error('two interval a');
+ });
+ var j = setInterval(function() {
+ clearInterval(j);
+ throw new Error('two interval b');
+ });
+ removeListener(listener);
+});
+
+expect.push('two interval a 2');
+expect.push('two interval b 2');
+addListener(listener);
+process.nextTick(function() {
+ var i = setInterval(function() {
+ clearInterval(i);
+ throw new Error('two interval a 2');
+ });
+ var j = setInterval(function() {
+ clearInterval(j);
+ throw new Error('two interval b 2');
+ });
+});
+removeListener(listener);
+
+expect.push('nest interval parent');
+expect.push('nest interval child');
+process.nextTick(function() {
+ addListener(listener);
+ var i = setInterval(function() {
+ clearInterval(i);
+ var j = setInterval(function() {
+ clearInterval(j);
+ throw new Error('nest interval child');
+ });
+ throw new Error('nest interval parent');
+ });
+ removeListener(listener);
+});
+
+expect.push('nest interval parent 2');
+expect.push('nest interval child 2');
+addListener(listener);
+process.nextTick(function() {
+ var i = setInterval(function() {
+ clearInterval(i);
+ var j = setInterval(function() {
+ clearInterval(j);
+ throw new Error('nest interval child 2');
+ });
+ throw new Error('nest interval parent 2');
+ });
+});
+removeListener(listener);
--
1.8.3.2
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.