Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
process: improve nextTick performance
No longer check process.exiting on each tick add.
Cleaner emitDestroy with try/finally.
No longer use Reflect.apply since it's slower.
  • Loading branch information
apapirovski committed Apr 22, 2019
commit 4d82fd811bd1df972f28da47280b23fc0aaca013
67 changes: 31 additions & 36 deletions lib/internal/process/task_queues.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use strict';

const { FunctionPrototype, Reflect } = primordials;
const { FunctionPrototype } = primordials;

const {
// For easy access to the nextTick state in the C++ land,
Expand Down Expand Up @@ -62,25 +62,24 @@ function processTicksAndRejections() {
let tock;
do {
while (tock = queue.shift()) {
const asyncId = tock[async_id_symbol];
emitBefore(asyncId, tock[trigger_async_id_symbol]);
// emitDestroy() places the async_id_symbol into an asynchronous queue
// that calls the destroy callback in the future. It's called before
// calling tock.callback so destroy will be called even if the callback
// throws an exception that is handled by 'uncaughtException' or a
// domain.
// TODO(trevnorris): This is a bit of a hack. It relies on the fact
// that nextTick() doesn't allow the event loop to proceed, but if
// any async hooks are enabled during the callback's execution then
// this tock's after hook will be called, but not its destroy hook.
if (destroyHooksExist())
emitDestroy(asyncId);

const callback = tock.callback;
if (tock.args === undefined)
callback();
else
Reflect.apply(callback, undefined, tock.args);
const {
[async_id_symbol]: asyncId,
[trigger_async_id_symbol]: triggerAsyncId,
callback,
args
} = tock;

emitBefore(asyncId, triggerAsyncId);

try {
if (args === undefined)
callback();
else
callback(...args);
} finally {
if (destroyHooksExist())
emitDestroy(asyncId);
}

emitAfter(asyncId);
}
Expand All @@ -91,23 +90,18 @@ function processTicksAndRejections() {
}

class TickObject {
constructor(callback, args, triggerAsyncId) {
constructor(callback, args, asyncId, triggerAsyncId) {
// This must be set to null first to avoid function tracking
// on the hidden class, revisit in V8 versions after 6.2
// on the hidden class, revisit in V8 versions after 7.4
this.callback = null;
this.callback = callback;
this.args = args;

const asyncId = newAsyncId();
this[async_id_symbol] = asyncId;
this[trigger_async_id_symbol] = triggerAsyncId;

if (initHooksExist()) {
emitInit(asyncId,
'TickObject',
triggerAsyncId,
this);
}
if (initHooksExist())
emitInit(asyncId, 'TickObject', triggerAsyncId, this);
}
}

Expand All @@ -117,24 +111,25 @@ function nextTick(callback) {
if (typeof callback !== 'function')
throw new ERR_INVALID_CALLBACK(callback);

if (process._exiting)
return;

var args;
let args;
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++)
for (let i = 1; i < arguments.length; i++)
args[i - 1] = arguments[i];
}

if (queue.isEmpty())
if (queue.isEmpty() && !process._exiting)
setHasTickScheduled(true);
queue.push(new TickObject(callback, args, getDefaultTriggerAsyncId()));

queue.push(new TickObject(callback,
args,
newAsyncId(),
getDefaultTriggerAsyncId()));
}

let AsyncResource;
Expand Down