diff --git a/doc/api/async_hooks.md b/doc/api/async_hooks.md index 84c9dd45b349..dc63446d0c13 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 53ce03823480..9a9559f9ba74 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 1f1dd82c37ac..4e5907342a7c 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 0ba26ce033e6..f1e8cb8db2ce 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 dd650d3c9acd..e95db9df362a 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 000000000000..93177da53191 --- /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);