|
|
@ -10,6 +10,42 @@ exports.setup = setupNextTick; |
|
|
|
// Will be overwritten when setupNextTick() is called.
|
|
|
|
exports.nextTick = null; |
|
|
|
|
|
|
|
class NextTickQueue { |
|
|
|
constructor() { |
|
|
|
this.head = null; |
|
|
|
this.tail = null; |
|
|
|
this.length = 0; |
|
|
|
} |
|
|
|
|
|
|
|
push(v) { |
|
|
|
const entry = { data: v, next: null }; |
|
|
|
if (this.length > 0) |
|
|
|
this.tail.next = entry; |
|
|
|
else |
|
|
|
this.head = entry; |
|
|
|
this.tail = entry; |
|
|
|
++this.length; |
|
|
|
} |
|
|
|
|
|
|
|
shift() { |
|
|
|
if (this.length === 0) |
|
|
|
return; |
|
|
|
const ret = this.head.data; |
|
|
|
if (this.length === 1) |
|
|
|
this.head = this.tail = null; |
|
|
|
else |
|
|
|
this.head = this.head.next; |
|
|
|
--this.length; |
|
|
|
return ret; |
|
|
|
} |
|
|
|
|
|
|
|
clear() { |
|
|
|
this.head = null; |
|
|
|
this.tail = null; |
|
|
|
this.length = 0; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
function setupNextTick() { |
|
|
|
const async_wrap = process.binding('async_wrap'); |
|
|
|
const async_hooks = require('async_hooks'); |
|
|
@ -27,7 +63,7 @@ function setupNextTick() { |
|
|
|
const { kInit, kBefore, kAfter, kDestroy, kAsyncUidCntr, kInitTriggerId } = |
|
|
|
async_wrap.constants; |
|
|
|
const { async_id_symbol, trigger_id_symbol } = async_wrap; |
|
|
|
var nextTickQueue = []; |
|
|
|
var nextTickQueue = new NextTickQueue(); |
|
|
|
var microtasksScheduled = false; |
|
|
|
|
|
|
|
// Used to run V8's micro task queue.
|
|
|
@ -55,27 +91,29 @@ function setupNextTick() { |
|
|
|
function tickDone() { |
|
|
|
if (tickInfo[kLength] !== 0) { |
|
|
|
if (tickInfo[kLength] <= tickInfo[kIndex]) { |
|
|
|
nextTickQueue = []; |
|
|
|
nextTickQueue.clear(); |
|
|
|
tickInfo[kLength] = 0; |
|
|
|
} else { |
|
|
|
nextTickQueue.splice(0, tickInfo[kIndex]); |
|
|
|
tickInfo[kLength] = nextTickQueue.length; |
|
|
|
} |
|
|
|
} |
|
|
|
tickInfo[kIndex] = 0; |
|
|
|
} |
|
|
|
|
|
|
|
const microTasksTickObject = { |
|
|
|
callback: runMicrotasksCallback, |
|
|
|
args: undefined, |
|
|
|
domain: null, |
|
|
|
[async_id_symbol]: 0, |
|
|
|
[trigger_id_symbol]: 0 |
|
|
|
}; |
|
|
|
function scheduleMicrotasks() { |
|
|
|
if (microtasksScheduled) |
|
|
|
return; |
|
|
|
|
|
|
|
const tickObject = |
|
|
|
new TickObject(runMicrotasksCallback, undefined, null); |
|
|
|
// For the moment all microtasks come from the void until the PromiseHook
|
|
|
|
// API is implemented.
|
|
|
|
tickObject[async_id_symbol] = 0; |
|
|
|
tickObject[trigger_id_symbol] = 0; |
|
|
|
nextTickQueue.push(tickObject); |
|
|
|
nextTickQueue.push(microTasksTickObject); |
|
|
|
|
|
|
|
tickInfo[kLength]++; |
|
|
|
microtasksScheduled = true; |
|
|
@ -86,8 +124,9 @@ function setupNextTick() { |
|
|
|
_runMicrotasks(); |
|
|
|
|
|
|
|
if (tickInfo[kIndex] < tickInfo[kLength] || |
|
|
|
emitPendingUnhandledRejections()) |
|
|
|
emitPendingUnhandledRejections()) { |
|
|
|
scheduleMicrotasks(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
function _combinedTickCallback(args, callback) { |
|
|
@ -133,7 +172,8 @@ function setupNextTick() { |
|
|
|
function _tickCallback() { |
|
|
|
do { |
|
|
|
while (tickInfo[kIndex] < tickInfo[kLength]) { |
|
|
|
const tock = nextTickQueue[tickInfo[kIndex]++]; |
|
|
|
++tickInfo[kIndex]; |
|
|
|
const tock = nextTickQueue.shift(); |
|
|
|
const callback = tock.callback; |
|
|
|
const args = tock.args; |
|
|
|
|
|
|
@ -174,7 +214,8 @@ function setupNextTick() { |
|
|
|
function _tickDomainCallback() { |
|
|
|
do { |
|
|
|
while (tickInfo[kIndex] < tickInfo[kLength]) { |
|
|
|
const tock = nextTickQueue[tickInfo[kIndex]++]; |
|
|
|
++tickInfo[kIndex]; |
|
|
|
const tock = nextTickQueue.shift(); |
|
|
|
const callback = tock.callback; |
|
|
|
const domain = tock.domain; |
|
|
|
const args = tock.args; |
|
|
@ -210,45 +251,48 @@ function setupNextTick() { |
|
|
|
} while (tickInfo[kLength] !== 0); |
|
|
|
} |
|
|
|
|
|
|
|
function TickObject(callback, args, domain) { |
|
|
|
this.callback = callback; |
|
|
|
this.domain = domain; |
|
|
|
this.args = args; |
|
|
|
this[async_id_symbol] = -1; |
|
|
|
this[trigger_id_symbol] = -1; |
|
|
|
} |
|
|
|
|
|
|
|
function setupInit(tickObject, triggerAsyncId) { |
|
|
|
tickObject[async_id_symbol] = ++async_uid_fields[kAsyncUidCntr]; |
|
|
|
tickObject[trigger_id_symbol] = triggerAsyncId || initTriggerId(); |
|
|
|
if (async_hook_fields[kInit] > 0) { |
|
|
|
emitInit(tickObject[async_id_symbol], |
|
|
|
'TickObject', |
|
|
|
tickObject[trigger_id_symbol], |
|
|
|
tickObject); |
|
|
|
class TickObject { |
|
|
|
constructor(callback, args, asyncId, triggerAsyncId) { |
|
|
|
this.callback = callback; |
|
|
|
this.args = args; |
|
|
|
this.domain = process.domain || null; |
|
|
|
this[async_id_symbol] = asyncId; |
|
|
|
this[trigger_id_symbol] = triggerAsyncId; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// `nextTick()` will not enqueue any callback when the process is about to
|
|
|
|
// exit since the callback would not have a chance to be executed.
|
|
|
|
function nextTick(callback) { |
|
|
|
if (typeof callback !== 'function') |
|
|
|
throw new errors.TypeError('ERR_INVALID_CALLBACK'); |
|
|
|
// on the way out, don't bother. it won't get fired anyway.
|
|
|
|
|
|
|
|
if (process._exiting) |
|
|
|
return; |
|
|
|
|
|
|
|
var args; |
|
|
|
if (arguments.length > 1) { |
|
|
|
args = new Array(arguments.length - 1); |
|
|
|
for (var i = 1; i < arguments.length; i++) |
|
|
|
args[i - 1] = arguments[i]; |
|
|
|
switch (arguments.length) { |
|
|
|
case 1: break; |
|
|
|
case 2: args = [arguments[1]]; break; |
|
|
|
case 3: args = [arguments[1], arguments[2]]; break; |
|
|
|
case 4: args = [arguments[1], arguments[2], arguments[3]]; break; |
|
|
|
default: |
|
|
|
args = new Array(arguments.length - 1); |
|
|
|
for (var i = 1; i < arguments.length; i++) |
|
|
|
args[i - 1] = arguments[i]; |
|
|
|
} |
|
|
|
|
|
|
|
var obj = new TickObject(callback, args, process.domain || null); |
|
|
|
setupInit(obj, null); |
|
|
|
const asyncId = ++async_uid_fields[kAsyncUidCntr]; |
|
|
|
const triggerAsyncId = initTriggerId(); |
|
|
|
const obj = new TickObject(callback, args, asyncId, triggerAsyncId); |
|
|
|
nextTickQueue.push(obj); |
|
|
|
tickInfo[kLength]++; |
|
|
|
++tickInfo[kLength]; |
|
|
|
if (async_hook_fields[kInit] > 0) |
|
|
|
emitInit(asyncId, 'TickObject', triggerAsyncId, obj); |
|
|
|
} |
|
|
|
|
|
|
|
// `internalNextTick()` will not enqueue any callback when the process is
|
|
|
|
// about to exit since the callback would not have a chance to be executed.
|
|
|
|
function internalNextTick(triggerAsyncId, callback) { |
|
|
|
if (typeof callback !== 'function') |
|
|
|
throw new TypeError('callback is not a function'); |
|
|
@ -259,17 +303,25 @@ function setupNextTick() { |
|
|
|
return; |
|
|
|
|
|
|
|
var args; |
|
|
|
if (arguments.length > 2) { |
|
|
|
args = new Array(arguments.length - 2); |
|
|
|
for (var i = 2; i < arguments.length; i++) |
|
|
|
args[i - 2] = arguments[i]; |
|
|
|
switch (arguments.length) { |
|
|
|
case 2: break; |
|
|
|
case 3: args = [arguments[2]]; break; |
|
|
|
case 4: args = [arguments[2], arguments[3]]; break; |
|
|
|
case 5: args = [arguments[2], arguments[3], arguments[4]]; break; |
|
|
|
default: |
|
|
|
args = new Array(arguments.length - 2); |
|
|
|
for (var i = 2; i < arguments.length; i++) |
|
|
|
args[i - 2] = arguments[i]; |
|
|
|
} |
|
|
|
|
|
|
|
var obj = new TickObject(callback, args, process.domain || null); |
|
|
|
setupInit(obj, triggerAsyncId); |
|
|
|
const asyncId = ++async_uid_fields[kAsyncUidCntr]; |
|
|
|
const obj = new TickObject(callback, args, asyncId, triggerAsyncId); |
|
|
|
nextTickQueue.push(obj); |
|
|
|
++tickInfo[kLength]; |
|
|
|
if (async_hook_fields[kInit] > 0) |
|
|
|
emitInit(asyncId, 'TickObject', triggerAsyncId, obj); |
|
|
|
|
|
|
|
// The call to initTriggerId() was skipped, so clear kInitTriggerId.
|
|
|
|
async_uid_fields[kInitTriggerId] = 0; |
|
|
|
nextTickQueue.push(obj); |
|
|
|
tickInfo[kLength]++; |
|
|
|
} |
|
|
|
} |
|
|
|