aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorBrian White <mscdex@mscdex.net>2017-06-03 23:28:57 -0400
committerBrian White <mscdex@mscdex.net>2017-06-17 10:38:24 -0400
commit460ee75f7ea36fdb5da15ce868a37a881e0a06d9 (patch)
tree2dae4e8fa097556d499dfb823a1b0c55113bc589 /lib
parent1e2905f46ab10af312fc7439ff1922654e411e7c (diff)
downloadandroid-node-v8-460ee75f7ea36fdb5da15ce868a37a881e0a06d9.tar.gz
android-node-v8-460ee75f7ea36fdb5da15ce868a37a881e0a06d9.tar.bz2
android-node-v8-460ee75f7ea36fdb5da15ce868a37a881e0a06d9.zip
process: improve nextTick() performance
PR-URL: https://github.com/nodejs/node/pull/13446 Reviewed-By: Refael Ackermann <refack@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Jeremiah Senkpiel <fishrock123@rocketmail.com> Reviewed-By: Evan Lucas <evanlucas@me.com>
Diffstat (limited to 'lib')
-rw-r--r--lib/internal/process/next_tick.js138
1 files changed, 95 insertions, 43 deletions
diff --git a/lib/internal/process/next_tick.js b/lib/internal/process/next_tick.js
index 61756f603c..e183c38263 100644
--- a/lib/internal/process/next_tick.js
+++ b/lib/internal/process/next_tick.js
@@ -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]++;
}
}