-
Notifications
You must be signed in to change notification settings - Fork 29.8k
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
allow passing custom async_id to node::AsyncWrap::AsyncWrap #14208
Changes from all commits
4321206
44dc449
d28d20c
e1eae3c
2b9b46c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -243,7 +243,7 @@ void AsyncWrap::EmitAfter(Environment* env, double async_id) { | |
class PromiseWrap : public AsyncWrap { | ||
public: | ||
PromiseWrap(Environment* env, Local<Object> object, bool silent) | ||
: AsyncWrap(env, object, PROVIDER_PROMISE, silent) { | ||
: AsyncWrap(env, object, silent) { | ||
MakeWeak(this); | ||
} | ||
size_t self_size() const override { return sizeof(*this); } | ||
|
@@ -451,7 +451,8 @@ void AsyncWrap::ClearAsyncIdStack(const FunctionCallbackInfo<Value>& args) { | |
void AsyncWrap::AsyncReset(const FunctionCallbackInfo<Value>& args) { | ||
AsyncWrap* wrap; | ||
ASSIGN_OR_RETURN_UNWRAP(&wrap, args.Holder()); | ||
wrap->AsyncReset(); | ||
double execution_async_id = args[0]->IsNumber() ? args[0]->NumberValue() : -1; | ||
wrap->AsyncReset(execution_async_id); | ||
} | ||
|
||
|
||
|
@@ -574,7 +575,7 @@ void LoadAsyncWrapperInfo(Environment* env) { | |
AsyncWrap::AsyncWrap(Environment* env, | ||
Local<Object> object, | ||
ProviderType provider, | ||
bool silent) | ||
double execution_async_id) | ||
: BaseObject(env, object), | ||
provider_type_(provider) { | ||
CHECK_NE(provider, PROVIDER_NONE); | ||
|
@@ -584,7 +585,23 @@ AsyncWrap::AsyncWrap(Environment* env, | |
persistent().SetWrapperClassId(NODE_ASYNC_ID_OFFSET + provider); | ||
|
||
// Use AsyncReset() call to execute the init() callbacks. | ||
AsyncReset(silent); | ||
AsyncReset(execution_async_id); | ||
} | ||
|
||
|
||
// This is specifically used by the PromiseWrap constructor. | ||
AsyncWrap::AsyncWrap(Environment* env, | ||
Local<Object> object, | ||
bool silent) | ||
: BaseObject(env, object), | ||
provider_type_(PROVIDER_PROMISE) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't like this. It is not clear from the constructor signature that this is only for promises. It would be better with a unified constructor function, like how There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't agree that adding another parameter to the constructor for a single case is a good idea. The general constructor is currently used in 12 locations outside of If everyone is absolutely against the new constructor then I'll figure out another way to have the constructor not call There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I can't follow that argument at all. To me, using default arguments is only an issue if there become too many of them, I don't see that happening here. Besides, your solution is just a different kind of default argument.
It is not clear from the calling code. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
If there's a parameter that will only ever be used by one other class, in the same file, I don't see a good reason that it should pollute the general constructor. Even if it has a default.
Don't follow what you're saying.
I'm really not worried about that since it's only called once from the same file. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
For the mere mortals who didn't write the file, it is actually a really big file that and it is hard for me to orient myself when reading it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @AndreasMadsen Does is not feel like a wart on the API to add a parameter with default value just to accommodate a class that isn't public? To make the intention of the alternative class constructor clear beyond the fact that friend class PromiseWrap;
// Constructor specifically for PromiseWrap.
AsyncWrap(Environment* env, v8::Local<v8::Object> promise, bool silent); so there would be no ambiguity as to why that alternative constructor exists, and so no other classes could accidentally call that constructor. I believe the intention of this code is reasonably clear. /cc @bnoordhuis if you're around, mind giving feedback on how you'd approach this scenario? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
No. At least not when it is just relaying the parameter to I think this is about minimizing complexity and readability. I think the default parameter is simultaneously a more simple solution and more readable. I'm not strong enough in C++ to give a better argument and "wart" is not concrete enough for me to argue against. I will let others take the argument from here. /cc @nodejs/async_hooks There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
At this point I think we just have a different aesthetic. IMO it's more clear to have a second documented constructor dedicated to a singular edge case usage than use an additional optional parameter. The key here is that in no foreseeable case will the optional parameter be used by anything other than There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. IMHO an adding a comment stating that this ctor is for use by |
||
CHECK_GE(object->InternalFieldCount(), 1); | ||
|
||
// Shift provider value over to prevent id collision. | ||
persistent().SetWrapperClassId(NODE_ASYNC_ID_OFFSET + provider_type_); | ||
|
||
// Use AsyncReset() call to execute the init() callbacks. | ||
AsyncReset(-1, silent); | ||
} | ||
|
||
|
||
|
@@ -596,8 +613,9 @@ AsyncWrap::~AsyncWrap() { | |
// Generalized call for both the constructor and for handles that are pooled | ||
// and reused over their lifetime. This way a new uid can be assigned when | ||
// the resource is pulled out of the pool and put back into use. | ||
void AsyncWrap::AsyncReset(bool silent) { | ||
async_id_ = env()->new_async_id(); | ||
void AsyncWrap::AsyncReset(double execution_async_id, bool silent) { | ||
async_id_ = | ||
execution_async_id == -1 ? env()->new_async_id() : execution_async_id; | ||
trigger_async_id_ = env()->get_init_trigger_async_id(); | ||
|
||
if (silent) return; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,6 +5,11 @@ const assert = require('assert'); | |
const async_hooks = require('async_hooks'); | ||
const binding = require(`./build/${common.buildType}/binding`); | ||
|
||
if (process.env.NODE_TEST_WITH_ASYNC_HOOKS) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What are the tradeoffs of this VS doing There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure what you mean. Mind clarifying? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we delete Will that make this testable? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. while the possibility does exist, we depend on env vars existing for other things that could also be deleted. So I'm not going to worry about it. |
||
common.skip('cannot test with env var NODE_TEST_WITH_ASYNC_HOOKS'); | ||
return; | ||
} | ||
|
||
// Baseline to make sure the internal field isn't being set. | ||
assert.strictEqual( | ||
binding.getPromiseField(Promise.resolve(1)), | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
$(PYTHON) tools/test.py --mode=release $(CI_ASYNC_HOOKS)
?