mirror of
https://github.com/nodejs/node.git
synced 2025-05-07 02:42:08 +00:00
nextTick: Handle tick callbacks after each tick
This commit is contained in:
parent
0109a9f90a
commit
4e5fe2d45a
61
src/node.js
61
src/node.js
@ -239,6 +239,8 @@
|
||||
|
||||
if (domain) domain.exit();
|
||||
|
||||
// process the nextTicks after each time we get called.
|
||||
process._tickCallback();
|
||||
return ret;
|
||||
};
|
||||
};
|
||||
@ -247,8 +249,19 @@
|
||||
var nextTickQueue = [];
|
||||
var nextTickIndex = 0;
|
||||
var inTick = false;
|
||||
var tickDepth = 0;
|
||||
|
||||
// the maximum number of times it'll process something like
|
||||
// nextTick(function f(){nextTick(f)})
|
||||
// It's unlikely, but not illegal, to hit this limit. When
|
||||
// that happens, it yields to libuv's tick spinner.
|
||||
// This is a loop counter, not a stack depth, so we aren't using
|
||||
// up lots of memory here. I/O can sneak in before nextTick if this
|
||||
// limit is hit, which is not ideal, but not terrible.
|
||||
process.maxTickDepth = 1000;
|
||||
|
||||
function tickDone() {
|
||||
tickDepth = 0;
|
||||
nextTickQueue.splice(0, nextTickIndex);
|
||||
nextTickIndex = 0;
|
||||
inTick = false;
|
||||
@ -259,28 +272,38 @@
|
||||
|
||||
process._tickCallback = function() {
|
||||
if (inTick) return;
|
||||
var nextTickLength = nextTickQueue.length;
|
||||
if (nextTickLength === 0) return;
|
||||
inTick = true;
|
||||
|
||||
while (nextTickIndex < nextTickLength) {
|
||||
var tock = nextTickQueue[nextTickIndex++];
|
||||
var callback = tock.callback;
|
||||
if (tock.domain) {
|
||||
if (tock.domain._disposed) continue;
|
||||
tock.domain.enter();
|
||||
// always do this at least once. otherwise if process.maxTickDepth
|
||||
// is set to some negative value, we'd never process any of them.
|
||||
do {
|
||||
tickDepth++;
|
||||
var nextTickLength = nextTickQueue.length;
|
||||
if (nextTickLength === 0) return tickDone();
|
||||
while (nextTickIndex < nextTickLength) {
|
||||
var tock = nextTickQueue[nextTickIndex++];
|
||||
var callback = tock.callback;
|
||||
if (tock.domain) {
|
||||
if (tock.domain._disposed) continue;
|
||||
tock.domain.enter();
|
||||
}
|
||||
var threw = true;
|
||||
try {
|
||||
callback();
|
||||
threw = false;
|
||||
} finally {
|
||||
if (threw) tickDone();
|
||||
}
|
||||
if (tock.domain) {
|
||||
tock.domain.exit();
|
||||
}
|
||||
}
|
||||
var threw = true;
|
||||
try {
|
||||
callback();
|
||||
threw = false;
|
||||
} finally {
|
||||
if (threw) tickDone();
|
||||
}
|
||||
if (tock.domain) {
|
||||
tock.domain.exit();
|
||||
}
|
||||
}
|
||||
nextTickQueue.splice(0, nextTickIndex);
|
||||
nextTickIndex = 0;
|
||||
|
||||
// continue until the max depth or we run out of tocks.
|
||||
} while (tickDepth < process.maxTickDepth &&
|
||||
nextTickQueue.length > 0);
|
||||
|
||||
tickDone();
|
||||
};
|
||||
|
64
test/simple/test-next-tick-intentional-starvation.js
Normal file
64
test/simple/test-next-tick-intentional-starvation.js
Normal file
@ -0,0 +1,64 @@
|
||||
// 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');
|
||||
|
||||
// this is the inverse of test-next-tick-starvation.
|
||||
// it verifies that process.nextTick will *always* come before other
|
||||
// events, up to the limit of the process.maxTickDepth value.
|
||||
|
||||
// WARNING: unsafe!
|
||||
process.maxTickDepth = Infinity;
|
||||
|
||||
var ran = false;
|
||||
var starved = false;
|
||||
var start = +new Date();
|
||||
var timerRan = false;
|
||||
|
||||
function spin() {
|
||||
ran = true;
|
||||
var now = +new Date();
|
||||
if (now - start > 100) {
|
||||
console.log('The timer is starving, just as we planned.');
|
||||
starved = true;
|
||||
|
||||
// now let it out.
|
||||
return;
|
||||
}
|
||||
|
||||
process.nextTick(spin);
|
||||
}
|
||||
|
||||
function onTimeout() {
|
||||
if (!starved) throw new Error('The timer escaped!');
|
||||
console.log('The timer ran once the ban was lifted');
|
||||
timerRan = true;
|
||||
}
|
||||
|
||||
spin();
|
||||
setTimeout(onTimeout, 50);
|
||||
|
||||
process.on('exit', function() {
|
||||
assert.ok(ran);
|
||||
assert.ok(starved);
|
||||
assert.ok(timerRan);
|
||||
});
|
@ -46,11 +46,16 @@ function expect(activeHandles, activeRequests) {
|
||||
expect(2, 1); // client handle doesn't shut down until next tick
|
||||
})();
|
||||
|
||||
// Force the nextTicks to be deferred to a later time.
|
||||
process.maxTickDepth = 1;
|
||||
|
||||
process.nextTick(function() {
|
||||
process.nextTick(function() {
|
||||
process.nextTick(function() {
|
||||
// the handles should be gone but the connect req could still be alive
|
||||
assert.equal(process._getActiveHandles().length, 0);
|
||||
process.nextTick(function() {
|
||||
// the handles should be gone but the connect req could still be alive
|
||||
assert.equal(process._getActiveHandles().length, 0);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user