async_hooks: remove promise object from resource
While it doesn't make any difference now. In the future PromiseHooks could be refactored to provide an asyncId instead of the promise object. That would make escape analysis on promises possible. Escape analysis on promises could lead to a more efficient destroy hook, if provide by PromiseHooks as well. But at the very least would allow the destroy hook to be emitted earlier. The destroy hook not being emitted on promises frequent enough is a known and reported issue. See https://github.com/nodejs/node/issues/14446 and https://github.com/Jeff-Lewis/cls-hooked/issues/11. While all this is speculation for now, it all depends on the promise object not being a part of the PromiseWrap resource object. Ref: https://github.com/nodejs/node/issues/14446 Ref: https://github.com/nodejs/diagnostics/issues/188 PR-URL: https://github.com/nodejs/node/pull/23443 Refs: https://github.com/nodejs/node/issues/14446 Refs: https://github.com/nodejs/diagnostics/issues/188 Reviewed-By: Benedikt Meurer <benedikt.meurer@gmail.com> Reviewed-By: Gus Caplan <me@gus.host> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: George Adams <george.adams@uk.ibm.com>
This commit is contained in:
parent
561e30d9ef
commit
1a2cf6696f
@ -301,8 +301,7 @@ currently not considered public, but using the Embedder API, users can provide
|
|||||||
and document their own resource objects. For example, such a resource object
|
and document their own resource objects. For example, such a resource object
|
||||||
could contain the SQL query being executed.
|
could contain the SQL query being executed.
|
||||||
|
|
||||||
In the case of Promises, the `resource` object will have `promise` property
|
In the case of Promises, the `resource` object will have an
|
||||||
that refers to the `Promise` that is being initialized, and an
|
|
||||||
`isChainedPromise` property, set to `true` if the promise has a parent promise,
|
`isChainedPromise` property, set to `true` if the promise has a parent promise,
|
||||||
and `false` otherwise. For example, in the case of `b = a.then(handler)`, `a` is
|
and `false` otherwise. For example, in the case of `b = a.then(handler)`, `a` is
|
||||||
considered a parent `Promise` of `b`. Here, `b` is considered a chained promise.
|
considered a parent `Promise` of `b`. Here, `b` is considered a chained promise.
|
||||||
|
@ -55,13 +55,6 @@ const asyncHook = createHook({
|
|||||||
// if this operation is created while in a domain, let's mark it
|
// if this operation is created while in a domain, let's mark it
|
||||||
pairing.set(asyncId, process.domain);
|
pairing.set(asyncId, process.domain);
|
||||||
resource.domain = process.domain;
|
resource.domain = process.domain;
|
||||||
if (resource.promise !== undefined &&
|
|
||||||
resource.promise instanceof Promise) {
|
|
||||||
// resource.promise instanceof Promise make sure that the
|
|
||||||
// promise comes from the same context
|
|
||||||
// see https://github.com/nodejs/node/issues/15673
|
|
||||||
resource.promise.domain = process.domain;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
before(asyncId) {
|
before(asyncId) {
|
||||||
|
@ -185,16 +185,13 @@ class PromiseWrap : public AsyncWrap {
|
|||||||
SET_MEMORY_INFO_NAME(PromiseWrap)
|
SET_MEMORY_INFO_NAME(PromiseWrap)
|
||||||
SET_SELF_SIZE(PromiseWrap)
|
SET_SELF_SIZE(PromiseWrap)
|
||||||
|
|
||||||
static constexpr int kPromiseField = 1;
|
static constexpr int kIsChainedPromiseField = 1;
|
||||||
static constexpr int kIsChainedPromiseField = 2;
|
static constexpr int kInternalFieldCount = 2;
|
||||||
static constexpr int kInternalFieldCount = 3;
|
|
||||||
|
|
||||||
static PromiseWrap* New(Environment* env,
|
static PromiseWrap* New(Environment* env,
|
||||||
Local<Promise> promise,
|
Local<Promise> promise,
|
||||||
PromiseWrap* parent_wrap,
|
PromiseWrap* parent_wrap,
|
||||||
bool silent);
|
bool silent);
|
||||||
static void GetPromise(Local<String> property,
|
|
||||||
const PropertyCallbackInfo<Value>& info);
|
|
||||||
static void getIsChainedPromise(Local<String> property,
|
static void getIsChainedPromise(Local<String> property,
|
||||||
const PropertyCallbackInfo<Value>& info);
|
const PropertyCallbackInfo<Value>& info);
|
||||||
};
|
};
|
||||||
@ -205,7 +202,6 @@ PromiseWrap* PromiseWrap::New(Environment* env,
|
|||||||
bool silent) {
|
bool silent) {
|
||||||
Local<Object> object = env->promise_wrap_template()
|
Local<Object> object = env->promise_wrap_template()
|
||||||
->NewInstance(env->context()).ToLocalChecked();
|
->NewInstance(env->context()).ToLocalChecked();
|
||||||
object->SetInternalField(PromiseWrap::kPromiseField, promise);
|
|
||||||
object->SetInternalField(PromiseWrap::kIsChainedPromiseField,
|
object->SetInternalField(PromiseWrap::kIsChainedPromiseField,
|
||||||
parent_wrap != nullptr ?
|
parent_wrap != nullptr ?
|
||||||
v8::True(env->isolate()) :
|
v8::True(env->isolate()) :
|
||||||
@ -215,11 +211,6 @@ PromiseWrap* PromiseWrap::New(Environment* env,
|
|||||||
return new PromiseWrap(env, object, silent);
|
return new PromiseWrap(env, object, silent);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PromiseWrap::GetPromise(Local<String> property,
|
|
||||||
const PropertyCallbackInfo<Value>& info) {
|
|
||||||
info.GetReturnValue().Set(info.Holder()->GetInternalField(kPromiseField));
|
|
||||||
}
|
|
||||||
|
|
||||||
void PromiseWrap::getIsChainedPromise(Local<String> property,
|
void PromiseWrap::getIsChainedPromise(Local<String> property,
|
||||||
const PropertyCallbackInfo<Value>& info) {
|
const PropertyCallbackInfo<Value>& info) {
|
||||||
info.GetReturnValue().Set(
|
info.GetReturnValue().Set(
|
||||||
@ -315,9 +306,6 @@ static void SetupHooks(const FunctionCallbackInfo<Value>& args) {
|
|||||||
Local<ObjectTemplate> promise_wrap_template = ctor->InstanceTemplate();
|
Local<ObjectTemplate> promise_wrap_template = ctor->InstanceTemplate();
|
||||||
promise_wrap_template->SetInternalFieldCount(
|
promise_wrap_template->SetInternalFieldCount(
|
||||||
PromiseWrap::kInternalFieldCount);
|
PromiseWrap::kInternalFieldCount);
|
||||||
promise_wrap_template->SetAccessor(
|
|
||||||
FIXED_ONE_BYTE_STRING(env->isolate(), "promise"),
|
|
||||||
PromiseWrap::GetPromise);
|
|
||||||
promise_wrap_template->SetAccessor(
|
promise_wrap_template->SetAccessor(
|
||||||
FIXED_ONE_BYTE_STRING(env->isolate(), "isChainedPromise"),
|
FIXED_ONE_BYTE_STRING(env->isolate(), "isChainedPromise"),
|
||||||
PromiseWrap::getIsChainedPromise);
|
PromiseWrap::getIsChainedPromise);
|
||||||
|
@ -4,7 +4,6 @@ const common = require('../common');
|
|||||||
const assert = require('assert');
|
const assert = require('assert');
|
||||||
const async_hooks = require('async_hooks');
|
const async_hooks = require('async_hooks');
|
||||||
const EXPECTED_INITS = 2;
|
const EXPECTED_INITS = 2;
|
||||||
let p_resource = null;
|
|
||||||
let p_er = null;
|
let p_er = null;
|
||||||
let p_inits = 0;
|
let p_inits = 0;
|
||||||
|
|
||||||
@ -23,7 +22,6 @@ const mustCallInit = common.mustCall(function init(id, type, tid, resource) {
|
|||||||
if (type !== 'PROMISE')
|
if (type !== 'PROMISE')
|
||||||
return;
|
return;
|
||||||
p_inits++;
|
p_inits++;
|
||||||
p_resource = resource.promise;
|
|
||||||
}, EXPECTED_INITS);
|
}, EXPECTED_INITS);
|
||||||
|
|
||||||
const hook = async_hooks.createHook({
|
const hook = async_hooks.createHook({
|
||||||
@ -36,7 +34,6 @@ new Promise(common.mustCall((res) => {
|
|||||||
})).then(common.mustCall((val) => {
|
})).then(common.mustCall((val) => {
|
||||||
hook.enable().enable();
|
hook.enable().enable();
|
||||||
const p = new Promise((res) => res(val));
|
const p = new Promise((res) => res(val));
|
||||||
assert.strictEqual(p, p_resource);
|
|
||||||
hook.disable();
|
hook.disable();
|
||||||
return p;
|
return p;
|
||||||
})).then(common.mustCall((val2) => {
|
})).then(common.mustCall((val2) => {
|
||||||
|
@ -21,11 +21,9 @@ async_hooks.createHook({
|
|||||||
}).enable();
|
}).enable();
|
||||||
|
|
||||||
const a = Promise.resolve(42);
|
const a = Promise.resolve(42);
|
||||||
const b = a.then(common.mustCall());
|
a.then(common.mustCall());
|
||||||
|
|
||||||
assert.strictEqual(initCalls[0].triggerId, 1);
|
assert.strictEqual(initCalls[0].triggerId, 1);
|
||||||
assert.strictEqual(initCalls[0].resource.isChainedPromise, false);
|
assert.strictEqual(initCalls[0].resource.isChainedPromise, false);
|
||||||
assert.strictEqual(initCalls[0].resource.promise, a);
|
|
||||||
assert.strictEqual(initCalls[1].triggerId, initCalls[0].id);
|
assert.strictEqual(initCalls[1].triggerId, initCalls[0].id);
|
||||||
assert.strictEqual(initCalls[1].resource.isChainedPromise, true);
|
assert.strictEqual(initCalls[1].resource.isChainedPromise, true);
|
||||||
assert.strictEqual(initCalls[1].resource.promise, b);
|
|
||||||
|
@ -50,7 +50,6 @@ const vm = require('vm');
|
|||||||
d2.run(common.mustCall(() => {
|
d2.run(common.mustCall(() => {
|
||||||
p.then(common.mustCall((v) => {
|
p.then(common.mustCall((v) => {
|
||||||
assert.strictEqual(process.domain, d2);
|
assert.strictEqual(process.domain, d2);
|
||||||
assert.strictEqual(p.domain, d1);
|
|
||||||
}));
|
}));
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
@ -64,9 +63,8 @@ const vm = require('vm');
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
d2.run(common.mustCall(() => {
|
d2.run(common.mustCall(() => {
|
||||||
p.then(p.domain.bind(common.mustCall((v) => {
|
p.then(d1.bind(common.mustCall((v) => {
|
||||||
assert.strictEqual(process.domain, d1);
|
assert.strictEqual(process.domain, d1);
|
||||||
assert.strictEqual(p.domain, d1);
|
|
||||||
})));
|
})));
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
@ -83,7 +81,6 @@ const vm = require('vm');
|
|||||||
d2.run(common.mustCall(() => {
|
d2.run(common.mustCall(() => {
|
||||||
p.then(common.mustCall((v) => {
|
p.then(common.mustCall((v) => {
|
||||||
assert.strictEqual(process.domain, d2);
|
assert.strictEqual(process.domain, d2);
|
||||||
assert.strictEqual(p.domain, d1);
|
|
||||||
}));
|
}));
|
||||||
}));
|
}));
|
||||||
}));
|
}));
|
||||||
@ -100,7 +97,6 @@ const vm = require('vm');
|
|||||||
d2.run(common.mustCall(() => {
|
d2.run(common.mustCall(() => {
|
||||||
p.catch(common.mustCall((v) => {
|
p.catch(common.mustCall((v) => {
|
||||||
assert.strictEqual(process.domain, d2);
|
assert.strictEqual(process.domain, d2);
|
||||||
assert.strictEqual(p.domain, d1);
|
|
||||||
}));
|
}));
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user