Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

async_hooks: expose list of types #13610

Closed
wants to merge 7 commits into from
Closed

Conversation

refack
Copy link
Contributor

@refack refack commented Jun 11, 2017

Ref: #13561 (comment)

Also refactor JS type names allocation, so it provided by asunc_hooks

/cc @nodejs/async_hooks

Checklist
  • make -j4 test (UNIX), or vcbuild test (Windows) passes
  • tests and/or benchmarks are included
  • documentation is changed or added
  • commit message follows commit guidelines
Affected core subsystem(s)

async_hooks,timers,process

@nodejs-github-bot nodejs-github-bot added async_hooks Issues and PRs related to the async hooks subsystem. timers Issues and PRs related to the timers subsystem / setImmediate, setInterval, setTimeout. labels Jun 11, 2017
@refack refack added the wip Issues and PRs that are still a work in progress. label Jun 11, 2017
@refack
Copy link
Contributor Author

refack commented Jun 11, 2017

WIP (needs docs, and tests)
I'm looking for early feedback.

@tniessen tniessen added the process Issues and PRs related to the process subsystem. label Jun 11, 2017
function _getTypeName(module, type) {
return Object.keys(JSSideProviders)
.find((k) => JSSideProviders[k] === `${module} ${type}`);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems unnecessarily complicated.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah definatly a kludge.
This is what I came up with in my current (sleep deprived) state 😧

I'm looking for a structure that:

  1. Can get the keys out easily
  2. can be queried for a specific name by modules and type.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’d suggest really just listing the types for now

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have a better idea: lookup by function:
_getTypeName(setTimeout), _getTypeName(setImmediate)
that way it's also useful to end users.
The question is will it be robust (no name conflicts)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The question is will it be robust (no name conflicts)

That depends on what exactly your idea is – just have this for timers and nextTick? That doesn’t seem much more useful than listing these as values. If you’re trying to do this for generic async providers, you’ll run into the problem that many public APIs (e.g. net.Socket) can init different async types.

that way it's also useful to end users.

I wouldn’t underscore-prefix it in that case

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@refack

I have a better idea: lookup by function:

As @addaleax said, if you want this to work generically only a path of pain and suffering awaits you. I spent a couple weeks trying to get this and a similar feature to work. Will never work reliably.

TBH I'm not loving adding the module name to each type. Feels like serious overkill to me.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@refack

I have a better idea: lookup by function:
As @addaleax said, if you want this to work generically only a path of pain and suffering awaits you. I spent a couple weeks trying to get this and a similar feature to work. Will never work reliably.

TBH I'm not loving adding the module name to each type. Feels like serious overkill to me.

I didn't have any bright ideas. I think that I'd invert the dependency:

function registerTypeName(type) {
}

This way implementers (or node modules) could inform us of the names they choose. We could warn about possible coalitions, but that is obviously timing dependant.

@jasnell jasnell added the semver-minor PRs that contain new features and should be released in the next minor version. label Jun 12, 2017
@@ -480,4 +496,6 @@ module.exports = {
emitBefore: emitBeforeS,
emitAfter: emitAfterS,
emitDestroy: emitDestroyS,
// internal
_getTypeName,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this is meant for internal only, I would prefer that it be named with a Symbol rather than introducing a new _-prefixed property. Or, better yet, move it to an appropriate lib/internal location.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

made it public

@trevnorris
Copy link
Contributor

trevnorris commented Jun 12, 2017

This feels too complicated. Types were always meant to be a single string. Let's not mess with that. If we absolutely want to include the module name then just add it in the string. e.g. timers/Immediate (still not a fan of this, but maybe could be convinced otherwise)

Though this PR gives me another idea. I like getTypes(), and possibly we include registerType() for users of AsyncResource(). Could use it to make sure two module authors don't have a collision of names or such. But that's outside the scope of this PR.

@trevnorris
Copy link
Contributor

@refack

This way implementers (or node modules) could inform us of the names they choose. We could warn about possible coalitions, but that is obviously timing dependant.

Holy hell, did you read my mind as I was typing out my last comment?

@addaleax
Copy link
Member

Tbh, just export Object.keys(binding.Providers).concat(['TickObject', 'Timeout', 'Immediate']) on the module, document it, test it, done; it doesn’t always need to be made more complicated than it is.

@refack
Copy link
Contributor Author

refack commented Jun 12, 2017

IMHO current imp is nice and compact.

Tbh, just export Object.keys(binding.Providers).concat(['TickObject', 'Timeout', 'Immediate']) on the module, document it, test it, done; it doesn’t always need to be made more complicated than it is.

Could go K.I.S.S. if current doesn't make sense...

@refack
Copy link
Contributor Author

refack commented Jun 12, 2017

Could use it to make sure two module authors don't have a collision of names or such. But that's outside the scope of this PR.

The copy and paste of 'TickObject', 'Timeout', 'Immediate' gave me a creepy crawly feeling, so I did the extra 6 lines.

Copy link
Member

@addaleax addaleax left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea, this is much better.

lib/timers.js Outdated
@@ -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],
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These changes are still confusing, imho. In any case, if you want to keep them, name them kTimeoutType or similar since they are constants. :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ack


function registerTypeName(type) {
if (JSProviders.has(type))
throw new errors.TypeError('ERR_ASYNC_PROVIDER_NAME', type);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here's some insanity, but could be useful for debugging:

const JSProviders = new Map();
function registerTypeName(type) {
  if (JSProviders.has(type))
    throw new errors.TypeError('ERR_ASYNC_PROVIDER_NAME', type);  // include the old stack somehow
  JSProviders.set(type, new Error().stack);
}

Not suggesting we implement this, but thought something like it could be helpful for identifying the bugger who is using my type name.

Copy link
Contributor Author

@refack refack Jun 12, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could do:

const JSProviders = new Map();
function registerTypeName(type) {
  if (JSProviders.has(type))
    throw JSProviders.get(type);
  JSProviders.set(type, new errors.TypeError('ERR_ASYNC_PROVIDER_NAME', type));
}

Will throw weird errors, good for the AsyncResource implementer, bad for a 6-degrees-of-separation user

Copy link
Contributor

@trevnorris trevnorris left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I worry about the overhead of getTypes() (I've used it before in init()), but will worry about improving that later. Just don't forget to name the constants for timers and nexttick. Thanks!

@refack
Copy link
Contributor Author

refack commented Jun 12, 2017

@refack refack removed the wip Issues and PRs that are still a work in progress. label Jun 13, 2017
@refack
Copy link
Contributor Author

refack commented Jun 13, 2017

added: REPLACEME
-->

* Returns: `{Array}` list of async resource type names
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The "list of async resource type names" is redundant considering it is documented two lines below.

added: REPLACEME
-->

* Returns: `{Array}` list of async resource type names
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The "list of async resource type names" is redundant considering it is documented two lines below.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some note about why these are useful would be helpful here.

@@ -428,6 +438,13 @@ const server = net.createServer(function onConnection(conn) {
});
```

#### `async_hooks.registerTypeName(type)`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added YAML annotation needed.

#### `async_hooks.registerTypeName(type)`

* `type` {string} a new type of async resource
* Returns {string} the type name to use
Copy link
Member

@TimothyGu TimothyGu Jun 13, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not just return undefined? The way it is documented can be interpreted to imply some processing has been done on type, while it is actually returned verbatim.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I find it convenient to do the same as is also already in this PR:

const kTickObjectType = async_hooks.registerTypeName('TickObject');

@@ -461,6 +462,18 @@ function init(asyncId, type, triggerId, resource) {
processing_hook = false;
}

const JSProviders = new Set();
function registerTypeName(type) {
if (JSProviders.has(type))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should make sure type isn't already contained in async_wrap.Providers either?


function getTypes() {
return Object.keys(async_wrap.Providers)
.concat(...JSProviders.keys());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need for .keys(). Set's iteration protocol returns its contents by default.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is certainly not the fastest method, but given that this shouldn't be a hot code path that should be ok

@@ -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()`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alphabetical order.

@@ -461,6 +462,18 @@ function init(asyncId, type, triggerId, resource) {
processing_hook = false;
}

const JSProviders = new Set();
function registerTypeName(type) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like to see a type check here, ensuring type is a string.

@@ -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()`

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unnecessary whitespace before the yaml comment

@AndreasMadsen
Copy link
Member

  • If this is still something we like to do, I think we should validate the type name in the AsyncResource constructor. Otherwise, there is no way to enforce this. Depending on where we are on breaking changes in experimental API that might have to be semver-major change. But we can then backport this to v8.x, just without the assertion.

  • I don't see any reason to make a specific JSProvider set, just initialize the set with async_wrap.Providers. That should make getTypes and the suggested assertion easier.

  • What is the use case for getTypes, is this something we really need? If not I would like to keep the API exposure minimal, it is so much easier to add this stuff later.

@trevnorris
Copy link
Contributor

@AndreasMadsen I like the idea of being able to register a type name and having it error if the name has already been registered. Though I also think the file name where the name was registered should be in the error message to help developers identify where it's happening (can see myself freaking out not being able to identify which dependency is using a specific name).

I'm impartial to how the type names are accessed, but it does seem that adding values to .Providers should be enough IMO.

@AndreasMadsen
Copy link
Member

I like the idea of being able to register a type name and having it error if the name has already been registered. Though I also think the file name where the name was registered should be in the error message to help developers identify where it's happening (can see myself freaking out not being able to identify which dependency is using a specific name).

Completely agree. I'm just saying that it has little value if the user can just pass whatever value they want in AsyncResource. Thus we also need to validate that the type input to AsyncResource is registered.

@BridgeAR
Copy link
Member

@refack this needs a rebase, the comments should be addressed and I am not sure if this is in a state that would land as is. Would you mind following up on this?

@refack refack self-assigned this Sep 23, 2017
@BridgeAR
Copy link
Member

Ping @refack

@BridgeAR
Copy link
Member

Closing due to long inactivity. @refack if you want to pursue this further, please feel free to reopen!

@BridgeAR BridgeAR closed this Nov 22, 2017
@refack refack removed their assignment Oct 12, 2018
@refack refack added the stalled Issues and PRs that are stalled. label Oct 29, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
async_hooks Issues and PRs related to the async hooks subsystem. process Issues and PRs related to the process subsystem. semver-minor PRs that contain new features and should be released in the next minor version. stalled Issues and PRs that are stalled. timers Issues and PRs related to the timers subsystem / setImmediate, setInterval, setTimeout.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants