-
Notifications
You must be signed in to change notification settings - Fork 29.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
async_wrap: use kTotals to enable PromiseHook
Keep a total of enabled hook callbacks in kTotals. This value is used to track whether node::PromiseHook (src/async-wrap.cc) should be enabled or disabled. Don't enable node::PromiseHook, using enablePromiseHook(), until a hook has been added. Then, using disablePromiseHook(), disable node::PromiseHook when all hooks have been disabled. Need to use a native test in order to check the internal field of the Promise and check for a PromiseWrap. PR-URL: #13509 Reviewed-By: Andreas Madsen <[email protected]> Reviewed-By: Anna Henningsen <[email protected]>
- Loading branch information
1 parent
96279e8
commit 2122e2f
Showing
7 changed files
with
177 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -344,6 +344,7 @@ class Environment { | |
kBefore, | ||
kAfter, | ||
kDestroy, | ||
kTotals, | ||
kFieldsCount, | ||
}; | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
#include <node.h> | ||
#include <v8.h> | ||
|
||
namespace { | ||
|
||
using v8::FunctionCallbackInfo; | ||
using v8::Isolate; | ||
using v8::Local; | ||
using v8::NewStringType; | ||
using v8::Object; | ||
using v8::Promise; | ||
using v8::String; | ||
using v8::Value; | ||
|
||
static void ThrowError(Isolate* isolate, const char* err_msg) { | ||
Local<String> str = String::NewFromOneByte( | ||
isolate, | ||
reinterpret_cast<const uint8_t*>(err_msg), | ||
NewStringType::kNormal).ToLocalChecked(); | ||
isolate->ThrowException(str); | ||
} | ||
|
||
static void GetPromiseField(const FunctionCallbackInfo<Value>& args) { | ||
auto isolate = args.GetIsolate(); | ||
|
||
if (!args[0]->IsPromise()) | ||
return ThrowError(isolate, "arg is not an Promise"); | ||
|
||
auto p = args[0].As<Promise>(); | ||
if (p->InternalFieldCount() < 1) | ||
return ThrowError(isolate, "Promise has no internal field"); | ||
|
||
auto l = p->GetInternalField(0); | ||
args.GetReturnValue().Set(l); | ||
} | ||
|
||
inline void Initialize(v8::Local<v8::Object> binding) { | ||
NODE_SET_METHOD(binding, "getPromiseField", GetPromiseField); | ||
} | ||
|
||
NODE_MODULE(binding, Initialize) | ||
|
||
} // anonymous namespace |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
{ | ||
'targets': [ | ||
{ | ||
'target_name': 'binding', | ||
'defines': [ 'V8_DEPRECATION_WARNINGS=1' ], | ||
'sources': [ 'binding.cc' ] | ||
} | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
'use strict'; | ||
|
||
const common = require('../../common'); | ||
const assert = require('assert'); | ||
const async_hooks = require('async_hooks'); | ||
const binding = require(`./build/${common.buildType}/binding`); | ||
|
||
// Baseline to make sure the internal field isn't being set. | ||
assert.strictEqual( | ||
binding.getPromiseField(Promise.resolve(1)), | ||
0, | ||
'Promise internal field used despite missing enabled AsyncHook'); | ||
|
||
const hook0 = async_hooks.createHook({}).enable(); | ||
|
||
// Check that no PromiseWrap is created when there are no hook callbacks. | ||
assert.strictEqual( | ||
binding.getPromiseField(Promise.resolve(1)), | ||
0, | ||
'Promise internal field used despite missing enabled AsyncHook'); | ||
|
||
hook0.disable(); | ||
|
||
let pwrap = null; | ||
const hook1 = async_hooks.createHook({ | ||
init(id, type, tid, resource) { | ||
pwrap = resource; | ||
} | ||
}).enable(); | ||
|
||
// Check that the internal field returns the same PromiseWrap passed to init(). | ||
assert.strictEqual( | ||
binding.getPromiseField(Promise.resolve(1)), | ||
pwrap, | ||
'Unexpected PromiseWrap'); | ||
|
||
hook1.disable(); | ||
|
||
// Check that internal fields are no longer being set. | ||
assert.strictEqual( | ||
binding.getPromiseField(Promise.resolve(1)), | ||
0, | ||
'Promise internal field used despite missing enabled AsyncHook'); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
'use strict'; | ||
|
||
const common = require('../common'); | ||
const assert = require('assert'); | ||
const async_hooks = require('async_hooks'); | ||
const EXPECTED_INITS = 2; | ||
let p_resource = null; | ||
let p_er = null; | ||
let p_inits = 0; | ||
|
||
// Not useful to place common.mustCall() around 'exit' event b/c it won't be | ||
// able to check it anway. | ||
process.on('exit', (code) => { | ||
if (code !== 0) | ||
return; | ||
if (p_er !== null) | ||
throw p_er; | ||
// Expecint exactly 2 PROMISE types to reach init. | ||
assert.strictEqual(p_inits, EXPECTED_INITS); | ||
}); | ||
|
||
const mustCallInit = common.mustCall(function init(id, type, tid, resource) { | ||
if (type !== 'PROMISE') | ||
return; | ||
p_inits++; | ||
p_resource = resource.promise; | ||
}, EXPECTED_INITS); | ||
|
||
const hook = async_hooks.createHook({ | ||
init: mustCallInit | ||
// Enable then disable to test whether disable() actually works. | ||
}).enable().disable().disable(); | ||
|
||
new Promise(common.mustCall((res) => { | ||
res(42); | ||
})).then(common.mustCall((val) => { | ||
hook.enable().enable(); | ||
const p = new Promise((res) => res(val)); | ||
assert.strictEqual(p, p_resource); | ||
hook.disable(); | ||
return p; | ||
})).then(common.mustCall((val2) => { | ||
hook.enable(); | ||
const p = new Promise((res) => res(val2)); | ||
hook.disable(); | ||
return p; | ||
})).catch((er) => p_er = er); |