diff --git a/benchmark/process/queue-microtask-breadth.js b/benchmark/process/queue-microtask-breadth.js new file mode 100644 index 00000000000000..8bb33f6fdee6cc --- /dev/null +++ b/benchmark/process/queue-microtask-breadth.js @@ -0,0 +1,21 @@ +'use strict'; + +const common = require('../common.js'); +const bench = common.createBenchmark(main, { + n: [4e5] +}); + +function main({ n }) { + var j = 0; + + function cb() { + j++; + if (j === n) + bench.end(n); + } + + bench.start(); + for (var i = 0; i < n; i++) { + queueMicrotask(cb); + } +} diff --git a/benchmark/process/queue-microtask-depth.js b/benchmark/process/queue-microtask-depth.js new file mode 100644 index 00000000000000..407feb1b3270c4 --- /dev/null +++ b/benchmark/process/queue-microtask-depth.js @@ -0,0 +1,17 @@ +'use strict'; +const common = require('../common.js'); +const bench = common.createBenchmark(main, { + n: [12e5] +}); + +function main({ n }) { + let counter = n; + bench.start(); + queueMicrotask(onNextTick); + function onNextTick() { + if (--counter) + queueMicrotask(onNextTick); + else + bench.end(n); + } +} diff --git a/lib/async_hooks.js b/lib/async_hooks.js index 107c92c97d670c..20e9d3ea99f6b3 100644 --- a/lib/async_hooks.js +++ b/lib/async_hooks.js @@ -25,6 +25,7 @@ const { emitBefore, emitAfter, emitDestroy, + initHooksExist, } = internal_async_hooks; // Get symbols @@ -144,29 +145,31 @@ class AsyncResource { throw new ERR_INVALID_ASYNC_ID('triggerAsyncId', triggerAsyncId); } - this[async_id_symbol] = newAsyncId(); + const asyncId = newAsyncId(); + this[async_id_symbol] = asyncId; this[trigger_async_id_symbol] = triggerAsyncId; + // This prop name (destroyed) has to be synchronized with C++ - this[destroyedSymbol] = { destroyed: false }; + const destroyed = { destroyed: false }; + this[destroyedSymbol] = destroyed; - emitInit( - this[async_id_symbol], type, this[trigger_async_id_symbol], this - ); + if (initHooksExist()) { + emitInit(asyncId, type, triggerAsyncId, this); + } if (!opts.requireManualDestroy) { - registerDestroyHook(this, this[async_id_symbol], this[destroyedSymbol]); + registerDestroyHook(this, asyncId, destroyed); } } runInAsyncScope(fn, thisArg, ...args) { - emitBefore(this[async_id_symbol], this[trigger_async_id_symbol]); - let ret; + const asyncId = this[async_id_symbol]; + emitBefore(asyncId, this[trigger_async_id_symbol]); try { - ret = Reflect.apply(fn, thisArg, args); + return Reflect.apply(fn, thisArg, args); } finally { - emitAfter(this[async_id_symbol]); + emitAfter(asyncId); } - return ret; } emitDestroy() { diff --git a/lib/internal/async_hooks.js b/lib/internal/async_hooks.js index fac887d0a5daac..09d99129f9d5e3 100644 --- a/lib/internal/async_hooks.js +++ b/lib/internal/async_hooks.js @@ -283,14 +283,11 @@ function defaultTriggerAsyncIdScope(triggerAsyncId, block, ...args) { const oldDefaultTriggerAsyncId = async_id_fields[kDefaultTriggerAsyncId]; async_id_fields[kDefaultTriggerAsyncId] = triggerAsyncId; - let ret; try { - ret = Reflect.apply(block, null, args); + return Reflect.apply(block, null, args); } finally { async_id_fields[kDefaultTriggerAsyncId] = oldDefaultTriggerAsyncId; } - - return ret; } diff --git a/lib/internal/process/task_queues.js b/lib/internal/process/task_queues.js index 9e4fba6d9df4f0..a89335a22ce07c 100644 --- a/lib/internal/process/task_queues.js +++ b/lib/internal/process/task_queues.js @@ -35,6 +35,8 @@ const { } = require('internal/errors').codes; const FixedQueue = require('internal/fixed_queue'); +const FunctionBind = Function.call.bind(Function.prototype.bind); + // *Must* match Environment::TickInfo::Fields in src/env.h. const kHasTickScheduled = 0; @@ -147,28 +149,32 @@ function createMicrotaskResource() { }); } +function runMicrotask() { + this.runInAsyncScope(() => { + const callback = this.callback; + try { + callback(); + } catch (error) { + // TODO(devsnek) remove this if + // https://bugs.chromium.org/p/v8/issues/detail?id=8326 + // is resolved such that V8 triggers the fatal exception + // handler for microtasks + triggerFatalException(error); + } finally { + this.emitDestroy(); + } + }); +} + function queueMicrotask(callback) { if (typeof callback !== 'function') { throw new ERR_INVALID_ARG_TYPE('callback', 'function', callback); } const asyncResource = createMicrotaskResource(); + asyncResource.callback = callback; - enqueueMicrotask(() => { - asyncResource.runInAsyncScope(() => { - try { - callback(); - } catch (error) { - // TODO(devsnek) remove this if - // https://bugs.chromium.org/p/v8/issues/detail?id=8326 - // is resolved such that V8 triggers the fatal exception - // handler for microtasks - triggerFatalException(error); - } finally { - asyncResource.emitDestroy(); - } - }); - }); + enqueueMicrotask(FunctionBind(runMicrotask, asyncResource)); } module.exports = {