From c81e97580f6a856311207dba47da8af6d56a82a9 Mon Sep 17 00:00:00 2001 From: Refael Ackermann Date: Sun, 11 Jun 2017 08:29:02 -0400 Subject: [PATCH 1/7] async_hooks: expose list of types * refactor JS type names allocation --- lib/async_hooks.js | 18 ++++++++++++++++++ lib/internal/process/next_tick.js | 3 ++- lib/timers.js | 9 ++++++--- 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/lib/async_hooks.js b/lib/async_hooks.js index 53ce0382348042..c3bf7820c77075 100644 --- a/lib/async_hooks.js +++ b/lib/async_hooks.js @@ -461,6 +461,21 @@ function init(asyncId, type, triggerId, resource) { processing_hook = false; } +const JSSideProviders = { + Timeout: 'timers Timeout', + Immediate: 'timers Immediate', + TickObject: 'next_tick TickObject' +}; + +function _getTypeName(module, type) { + return Object.keys(JSSideProviders) + .find((k) => JSSideProviders[k] === `${module} ${type}`); +} + +function getTypes() { + return Object.keys(async_wrap.Providers) + .concat(Object.keys(JSSideProviders)); +} // Placing all exports down here because the exported classes won't export // otherwise. @@ -469,6 +484,7 @@ module.exports = { createHook, currentId, triggerId, + getTypes, // Embedder API AsyncResource, runInAsyncIdScope, @@ -480,4 +496,6 @@ module.exports = { emitBefore: emitBeforeS, emitAfter: emitAfterS, emitDestroy: emitDestroyS, + // internal + _getTypeName, }; diff --git a/lib/internal/process/next_tick.js b/lib/internal/process/next_tick.js index 0ba26ce033e6ff..a13cb933e53a72 100644 --- a/lib/internal/process/next_tick.js +++ b/lib/internal/process/next_tick.js @@ -27,6 +27,7 @@ function setupNextTick() { const { kInit, kBefore, kAfter, kDestroy, kAsyncUidCntr, kInitTriggerId } = async_wrap.constants; const { async_id_symbol, trigger_id_symbol } = async_wrap; + const asyncTypeTO = async_hooks._getTypeName('next_tick', 'TickObject'); var nextTickQueue = []; var microtasksScheduled = false; @@ -223,7 +224,7 @@ function setupNextTick() { tickObject[trigger_id_symbol] = triggerId || initTriggerId(); if (async_hook_fields[kInit] > 0) { emitInit(tickObject[async_id_symbol], - 'TickObject', + asyncTypeTO, tickObject[trigger_id_symbol], tickObject); } diff --git a/lib/timers.js b/lib/timers.js index dd650d3c9acd56..d5013b832d34ab 100644 --- a/lib/timers.js +++ b/lib/timers.js @@ -44,6 +44,8 @@ const { kInit, kBefore, kAfter, kDestroy, kAsyncUidCntr } = // Symbols for storing async id state. const async_id_symbol = Symbol('asyncId'); const trigger_id_symbol = Symbol('triggerId'); +const asyncTypeTO = async_hooks._getTypeName('timers', 'Timeout'); +const asyncTypeIm = async_hooks._getTypeName('timers', 'Immediate'); // Timeout values > TIMEOUT_MAX are set to 1. const TIMEOUT_MAX = 2147483647; // 2^31-1 @@ -190,7 +192,8 @@ function insert(item, unrefed) { item[async_id_symbol] = ++async_uid_fields[kAsyncUidCntr]; item[trigger_id_symbol] = initTriggerId(); if (async_hook_fields[kInit] > 0) - emitInit(item[async_id_symbol], 'Timeout', item[trigger_id_symbol], item); + emitInit(item[async_id_symbol], asyncTypeTO, item[trigger_id_symbol], + item); } L.append(list, item); @@ -589,7 +592,7 @@ function Timeout(after, callback, args) { this[async_id_symbol] = ++async_uid_fields[kAsyncUidCntr]; this[trigger_id_symbol] = initTriggerId(); if (async_hook_fields[kInit] > 0) - emitInit(this[async_id_symbol], 'Timeout', this[trigger_id_symbol], this); + emitInit(this[async_id_symbol], asyncTypeTO, this[trigger_id_symbol], this); } @@ -824,7 +827,7 @@ function Immediate() { this[async_id_symbol] = ++async_uid_fields[kAsyncUidCntr]; this[trigger_id_symbol] = initTriggerId(); if (async_hook_fields[kInit] > 0) - emitInit(this[async_id_symbol], 'Immediate', this[trigger_id_symbol], this); + emitInit(this[async_id_symbol], asyncTypeIm, this[trigger_id_symbol], this); } function setImmediate(callback, arg1, arg2, arg3) { From 2813840c66a03f9073178befa1d54eb33253aec5 Mon Sep 17 00:00:00 2001 From: Refael Ackermann Date: Mon, 12 Jun 2017 18:52:45 -0400 Subject: [PATCH 2/7] invert dependency from _getTypeName to registerTypeName --- lib/async_hooks.js | 20 +++++++++----------- lib/internal/errors.js | 1 + lib/internal/process/next_tick.js | 2 +- lib/timers.js | 4 ++-- 4 files changed, 13 insertions(+), 14 deletions(-) diff --git a/lib/async_hooks.js b/lib/async_hooks.js index c3bf7820c77075..ff833cf752ba59 100644 --- a/lib/async_hooks.js +++ b/lib/async_hooks.js @@ -1,5 +1,6 @@ 'use strict'; +const errors = require('internal/errors'); const async_wrap = process.binding('async_wrap'); /* Both these arrays are used to communicate between JS and C++ with as little * overhead as possible. @@ -461,20 +462,18 @@ function init(asyncId, type, triggerId, resource) { processing_hook = false; } -const JSSideProviders = { - Timeout: 'timers Timeout', - Immediate: 'timers Immediate', - TickObject: 'next_tick TickObject' -}; +const JSProviders = new Set(); -function _getTypeName(module, type) { - return Object.keys(JSSideProviders) - .find((k) => JSSideProviders[k] === `${module} ${type}`); +function registerTypeName(type) { + if (JSProviders.has(type)) + throw new errors.TypeError('ERR_ASYNC_PROVIDER_NAME', type); + JSProviders.add(type); + return type; } function getTypes() { return Object.keys(async_wrap.Providers) - .concat(Object.keys(JSSideProviders)); + .concat(...JSProviders.values()); } // Placing all exports down here because the exported classes won't export @@ -485,6 +484,7 @@ module.exports = { currentId, triggerId, getTypes, + registerTypeName, // Embedder API AsyncResource, runInAsyncIdScope, @@ -496,6 +496,4 @@ module.exports = { emitBefore: emitBeforeS, emitAfter: emitAfterS, emitDestroy: emitDestroyS, - // internal - _getTypeName, }; diff --git a/lib/internal/errors.js b/lib/internal/errors.js index 1f1dd82c37acb7..28e6019ecdc657 100644 --- a/lib/internal/errors.js +++ b/lib/internal/errors.js @@ -112,6 +112,7 @@ module.exports = exports = { // Note: Please try to keep these in alphabetical order E('ERR_ARG_NOT_ITERABLE', '%s must be iterable'); E('ERR_ASSERTION', (msg) => msg); +E('ERR_ASYNC_PROVIDER_NAME', '%s type name already registered'); E('ERR_CONSOLE_WRITABLE_STREAM', (name) => `Console expects a writable stream instance for ${name}`); E('ERR_CPU_USAGE', (errMsg) => `Unable to obtain cpu usage ${errMsg}`); diff --git a/lib/internal/process/next_tick.js b/lib/internal/process/next_tick.js index a13cb933e53a72..ea1eb5fde06537 100644 --- a/lib/internal/process/next_tick.js +++ b/lib/internal/process/next_tick.js @@ -27,7 +27,7 @@ function setupNextTick() { const { kInit, kBefore, kAfter, kDestroy, kAsyncUidCntr, kInitTriggerId } = async_wrap.constants; const { async_id_symbol, trigger_id_symbol } = async_wrap; - const asyncTypeTO = async_hooks._getTypeName('next_tick', 'TickObject'); + const asyncTypeTO = async_hooks.registerTypeName('TickObject'); var nextTickQueue = []; var microtasksScheduled = false; diff --git a/lib/timers.js b/lib/timers.js index d5013b832d34ab..0d5bccfd3e4c68 100644 --- a/lib/timers.js +++ b/lib/timers.js @@ -44,8 +44,8 @@ const { kInit, kBefore, kAfter, kDestroy, kAsyncUidCntr } = // Symbols for storing async id state. const async_id_symbol = Symbol('asyncId'); const trigger_id_symbol = Symbol('triggerId'); -const asyncTypeTO = async_hooks._getTypeName('timers', 'Timeout'); -const asyncTypeIm = async_hooks._getTypeName('timers', 'Immediate'); +const asyncTypeTO = async_hooks.registerTypeName('Timeout'); +const asyncTypeIm = async_hooks.registerTypeName('Immediate'); // Timeout values > TIMEOUT_MAX are set to 1. const TIMEOUT_MAX = 2147483647; // 2^31-1 From df0a172b213e727e3908c104d2b59bcbe46bf57e Mon Sep 17 00:00:00 2001 From: Refael Ackermann Date: Mon, 12 Jun 2017 19:29:14 -0400 Subject: [PATCH 3/7] a spot of cleanup --- lib/async_hooks.js | 3 +-- lib/internal/process/next_tick.js | 4 ++-- lib/timers.js | 10 +++++----- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/lib/async_hooks.js b/lib/async_hooks.js index ff833cf752ba59..9a9559f9ba74fc 100644 --- a/lib/async_hooks.js +++ b/lib/async_hooks.js @@ -463,7 +463,6 @@ function init(asyncId, type, triggerId, resource) { } const JSProviders = new Set(); - function registerTypeName(type) { if (JSProviders.has(type)) throw new errors.TypeError('ERR_ASYNC_PROVIDER_NAME', type); @@ -473,7 +472,7 @@ function registerTypeName(type) { function getTypes() { return Object.keys(async_wrap.Providers) - .concat(...JSProviders.values()); + .concat(...JSProviders.keys()); } // Placing all exports down here because the exported classes won't export diff --git a/lib/internal/process/next_tick.js b/lib/internal/process/next_tick.js index ea1eb5fde06537..f1e8cb8db2ced7 100644 --- a/lib/internal/process/next_tick.js +++ b/lib/internal/process/next_tick.js @@ -27,7 +27,7 @@ function setupNextTick() { const { kInit, kBefore, kAfter, kDestroy, kAsyncUidCntr, kInitTriggerId } = async_wrap.constants; const { async_id_symbol, trigger_id_symbol } = async_wrap; - const asyncTypeTO = async_hooks.registerTypeName('TickObject'); + const kTickObjectType = async_hooks.registerTypeName('TickObject'); var nextTickQueue = []; var microtasksScheduled = false; @@ -224,7 +224,7 @@ function setupNextTick() { tickObject[trigger_id_symbol] = triggerId || initTriggerId(); if (async_hook_fields[kInit] > 0) { emitInit(tickObject[async_id_symbol], - asyncTypeTO, + kTickObjectType, tickObject[trigger_id_symbol], tickObject); } diff --git a/lib/timers.js b/lib/timers.js index 0d5bccfd3e4c68..e95db9df362aec 100644 --- a/lib/timers.js +++ b/lib/timers.js @@ -44,8 +44,8 @@ const { kInit, kBefore, kAfter, kDestroy, kAsyncUidCntr } = // Symbols for storing async id state. const async_id_symbol = Symbol('asyncId'); const trigger_id_symbol = Symbol('triggerId'); -const asyncTypeTO = async_hooks.registerTypeName('Timeout'); -const asyncTypeIm = async_hooks.registerTypeName('Immediate'); +const kTimeoutType = async_hooks.registerTypeName('Timeout'); +const kImmediateType = async_hooks.registerTypeName('Immediate'); // Timeout values > TIMEOUT_MAX are set to 1. const TIMEOUT_MAX = 2147483647; // 2^31-1 @@ -192,7 +192,7 @@ function insert(item, unrefed) { item[async_id_symbol] = ++async_uid_fields[kAsyncUidCntr]; item[trigger_id_symbol] = initTriggerId(); if (async_hook_fields[kInit] > 0) - emitInit(item[async_id_symbol], asyncTypeTO, item[trigger_id_symbol], + emitInit(item[async_id_symbol], kTimeoutType, item[trigger_id_symbol], item); } @@ -592,7 +592,7 @@ function Timeout(after, callback, args) { this[async_id_symbol] = ++async_uid_fields[kAsyncUidCntr]; this[trigger_id_symbol] = initTriggerId(); if (async_hook_fields[kInit] > 0) - emitInit(this[async_id_symbol], asyncTypeTO, this[trigger_id_symbol], this); + emitInit(this[async_id_symbol], kTimeoutType, this[trigger_id_symbol], this); } @@ -827,7 +827,7 @@ function Immediate() { this[async_id_symbol] = ++async_uid_fields[kAsyncUidCntr]; this[trigger_id_symbol] = initTriggerId(); if (async_hook_fields[kInit] > 0) - emitInit(this[async_id_symbol], asyncTypeIm, this[trigger_id_symbol], this); + emitInit(this[async_id_symbol], kImmediateType, this[trigger_id_symbol], this); } function setImmediate(callback, arg1, arg2, arg3) { From 65b17b985ed4b69310ae8243e0e7ea1cefd71212 Mon Sep 17 00:00:00 2001 From: Refael Ackermann Date: Mon, 12 Jun 2017 19:29:28 -0400 Subject: [PATCH 4/7] test & doc --- doc/api/async_hooks.md | 17 ++++++++++ .../test-async-hooks-type-registry.js | 31 +++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 test/parallel/test-async-hooks-type-registry.js diff --git a/doc/api/async_hooks.md b/doc/api/async_hooks.md index 84c9dd45b34975..dc63446d0c13f3 100644 --- a/doc/api/async_hooks.md +++ b/doc/api/async_hooks.md @@ -131,6 +131,16 @@ provided by AsyncHooks itself. The logging should then be skipped when it was the logging itself that caused AsyncHooks callback to call. By doing this the otherwise infinite recursion is broken. +#### `async_hooks.getTypes()` + + + +* Returns: `{Array}` list of async resource type names + +Return the list of async resource type names. Names are simple strings. + #### `asyncHook.enable()` * Returns {AsyncHook} A reference to `asyncHook`. @@ -428,6 +438,13 @@ const server = net.createServer(function onConnection(conn) { }); ``` +#### `async_hooks.registerTypeName(type)` + +* `type` {string} a new type of async resource +* Returns {string} the type name to use + +Registers the type name for a new async resource. + #### `async_hooks.triggerId()` * Returns {number} the ID of the resource responsible for calling the callback diff --git a/test/parallel/test-async-hooks-type-registry.js b/test/parallel/test-async-hooks-type-registry.js new file mode 100644 index 00000000000000..3898386bcf049d --- /dev/null +++ b/test/parallel/test-async-hooks-type-registry.js @@ -0,0 +1,31 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const async_hooks = require('async_hooks'); + +const types1 = async_hooks.getTypes(); +types1.forEach((v, k) => assert.strictEqual( + typeof v, + 'string', + `${v} from types[${k}] should be a string`) +); + +const sillyName = 'gaga'; +const newType = async_hooks.getTypes(sillyName); +assert.strictEqual( + typeof newType, + 'string', + `${newType} should be a string` +); +assert.strictEqual(newType, sillyName); + +assert.throws(() => async_hooks.getTypes(sillyName), + common.expectsError( + 'ERR_ASYNC_PROVIDER_NAME', + TypeError, + `${sillyName} type name already registered` + )); + +const types2 = async_hooks.getTypes(); +assert.strictEqual(types2.length, types1 + 1); +assert.strictEqual(types2.includes(newType), true); From 8f5d9725c01edb73e6f4f6b476eaf5ab9368ab29 Mon Sep 17 00:00:00 2001 From: Refael Ackermann Date: Mon, 12 Jun 2017 20:12:52 -0400 Subject: [PATCH 5/7] senior moment with test code --- .../test-async-hooks-type-registry.js | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/test/parallel/test-async-hooks-type-registry.js b/test/parallel/test-async-hooks-type-registry.js index 3898386bcf049d..1b586da4b68c12 100644 --- a/test/parallel/test-async-hooks-type-registry.js +++ b/test/parallel/test-async-hooks-type-registry.js @@ -11,20 +11,23 @@ types1.forEach((v, k) => assert.strictEqual( ); const sillyName = 'gaga'; -const newType = async_hooks.getTypes(sillyName); +const newType = async_hooks.registerTypeName(sillyName); assert.strictEqual( typeof newType, 'string', `${newType} should be a string` ); assert.strictEqual(newType, sillyName); - -assert.throws(() => async_hooks.getTypes(sillyName), - common.expectsError( - 'ERR_ASYNC_PROVIDER_NAME', - TypeError, - `${sillyName} type name already registered` - )); +assert.throws( + () => async_hooks.registerTypeName(sillyName), + common.expectsError( + { + code: 'ERR_ASYNC_PROVIDER_NAME', + type: TypeError, + message: `"${sillyName}" type name already registered` + } + ) +); const types2 = async_hooks.getTypes(); assert.strictEqual(types2.length, types1 + 1); From b77ba7a75795e4b095f3c17b8d986bcc778464d4 Mon Sep 17 00:00:00 2001 From: Refael Ackermann Date: Mon, 12 Jun 2017 20:45:07 -0400 Subject: [PATCH 6/7] changes error --- lib/internal/errors.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/internal/errors.js b/lib/internal/errors.js index 28e6019ecdc657..4e5907342a7ce1 100644 --- a/lib/internal/errors.js +++ b/lib/internal/errors.js @@ -112,7 +112,7 @@ module.exports = exports = { // Note: Please try to keep these in alphabetical order E('ERR_ARG_NOT_ITERABLE', '%s must be iterable'); E('ERR_ASSERTION', (msg) => msg); -E('ERR_ASYNC_PROVIDER_NAME', '%s type name already registered'); +E('ERR_ASYNC_PROVIDER_NAME', '"%s" type name already registered'); E('ERR_CONSOLE_WRITABLE_STREAM', (name) => `Console expects a writable stream instance for ${name}`); E('ERR_CPU_USAGE', (errMsg) => `Unable to obtain cpu usage ${errMsg}`); From 38081744fa26ad496c4057414ec924db44750ffb Mon Sep 17 00:00:00 2001 From: Refael Ackermann Date: Mon, 12 Jun 2017 20:52:05 -0400 Subject: [PATCH 7/7] that's it I'm moving to ubuntu --- test/parallel/test-async-hooks-type-registry.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/parallel/test-async-hooks-type-registry.js b/test/parallel/test-async-hooks-type-registry.js index 1b586da4b68c12..93177da531914c 100644 --- a/test/parallel/test-async-hooks-type-registry.js +++ b/test/parallel/test-async-hooks-type-registry.js @@ -30,5 +30,5 @@ assert.throws( ); const types2 = async_hooks.getTypes(); -assert.strictEqual(types2.length, types1 + 1); +assert.strictEqual(types2.length, types1.length + 1); assert.strictEqual(types2.includes(newType), true);