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/lib/async_hooks.js b/lib/async_hooks.js index 53ce0382348042..9a9559f9ba74fc 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,6 +462,18 @@ function init(asyncId, type, triggerId, resource) { processing_hook = false; } +const JSProviders = new Set(); +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(...JSProviders.keys()); +} // Placing all exports down here because the exported classes won't export // otherwise. @@ -469,6 +482,8 @@ module.exports = { createHook, currentId, triggerId, + getTypes, + registerTypeName, // Embedder API AsyncResource, runInAsyncIdScope, diff --git a/lib/internal/errors.js b/lib/internal/errors.js index 1f1dd82c37acb7..4e5907342a7ce1 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 0ba26ce033e6ff..f1e8cb8db2ced7 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 kTickObjectType = async_hooks.registerTypeName('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', + kTickObjectType, tickObject[trigger_id_symbol], tickObject); } diff --git a/lib/timers.js b/lib/timers.js index dd650d3c9acd56..e95db9df362aec 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 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 @@ -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], kTimeoutType, 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], kTimeoutType, 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], kImmediateType, this[trigger_id_symbol], this); } function setImmediate(callback, arg1, arg2, arg3) { 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..93177da531914c --- /dev/null +++ b/test/parallel/test-async-hooks-type-registry.js @@ -0,0 +1,34 @@ +'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.registerTypeName(sillyName); +assert.strictEqual( + typeof newType, + 'string', + `${newType} should be a string` +); +assert.strictEqual(newType, sillyName); +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.length + 1); +assert.strictEqual(types2.includes(newType), true);