mirror of
https://github.com/nodejs/node.git
synced 2025-05-07 15:35:41 +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();
|
if (domain) domain.exit();
|
||||||
|
|
||||||
|
// process the nextTicks after each time we get called.
|
||||||
|
process._tickCallback();
|
||||||
return ret;
|
return ret;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@ -247,8 +249,19 @@
|
|||||||
var nextTickQueue = [];
|
var nextTickQueue = [];
|
||||||
var nextTickIndex = 0;
|
var nextTickIndex = 0;
|
||||||
var inTick = false;
|
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() {
|
function tickDone() {
|
||||||
|
tickDepth = 0;
|
||||||
nextTickQueue.splice(0, nextTickIndex);
|
nextTickQueue.splice(0, nextTickIndex);
|
||||||
nextTickIndex = 0;
|
nextTickIndex = 0;
|
||||||
inTick = false;
|
inTick = false;
|
||||||
@ -259,28 +272,38 @@
|
|||||||
|
|
||||||
process._tickCallback = function() {
|
process._tickCallback = function() {
|
||||||
if (inTick) return;
|
if (inTick) return;
|
||||||
var nextTickLength = nextTickQueue.length;
|
|
||||||
if (nextTickLength === 0) return;
|
|
||||||
inTick = true;
|
inTick = true;
|
||||||
|
|
||||||
while (nextTickIndex < nextTickLength) {
|
// always do this at least once. otherwise if process.maxTickDepth
|
||||||
var tock = nextTickQueue[nextTickIndex++];
|
// is set to some negative value, we'd never process any of them.
|
||||||
var callback = tock.callback;
|
do {
|
||||||
if (tock.domain) {
|
tickDepth++;
|
||||||
if (tock.domain._disposed) continue;
|
var nextTickLength = nextTickQueue.length;
|
||||||
tock.domain.enter();
|
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;
|
nextTickQueue.splice(0, nextTickIndex);
|
||||||
try {
|
nextTickIndex = 0;
|
||||||
callback();
|
|
||||||
threw = false;
|
// continue until the max depth or we run out of tocks.
|
||||||
} finally {
|
} while (tickDepth < process.maxTickDepth &&
|
||||||
if (threw) tickDone();
|
nextTickQueue.length > 0);
|
||||||
}
|
|
||||||
if (tock.domain) {
|
|
||||||
tock.domain.exit();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tickDone();
|
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
|
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() {
|
process.nextTick(function() {
|
||||||
process.nextTick(function() {
|
process.nextTick(function() {
|
||||||
// the handles should be gone but the connect req could still be alive
|
process.nextTick(function() {
|
||||||
assert.equal(process._getActiveHandles().length, 0);
|
// 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