async_wrap: call callback in destructor

Call a user's callback to notify that the handle has been destroyed.
Only pass the id of the AsyncWrap instance since the object no longer
exists.

The object that's being destructed should never be inspected within the
callback or any time afterward.

This commit make a breaking change. The init callback will now be passed
arguments in the order of provider, id, parent.

PR-URL: https://github.com/nodejs/node/pull/3461
Reviewed-By: Fedor Indutny <fedor@indutny.com>
This commit is contained in:
Trevor Norris 2015-10-20 12:20:10 -06:00
parent 80a66ba6ae
commit bb1bd76395
6 changed files with 25 additions and 4 deletions

View File

@ -41,11 +41,12 @@ inline AsyncWrap::AsyncWrap(Environment* env,
v8::Local<v8::Value> argv[] = {
v8::Int32::New(env->isolate(), provider),
v8::Integer::New(env->isolate(), get_uid()),
Null(env->isolate())
};
if (parent != nullptr)
argv[1] = parent->object();
argv[2] = parent->object();
v8::MaybeLocal<v8::Value> ret =
init_fn->Call(env->context(), object, ARRAY_SIZE(argv), argv);
@ -57,6 +58,22 @@ inline AsyncWrap::AsyncWrap(Environment* env,
}
inline AsyncWrap::~AsyncWrap() {
if (!ran_init_callback())
return;
v8::Local<v8::Function> fn = env()->async_hooks_destroy_function();
if (!fn.IsEmpty()) {
v8::HandleScope scope(env()->isolate());
v8::Local<v8::Value> uid = v8::Integer::New(env()->isolate(), get_uid());
v8::MaybeLocal<v8::Value> ret =
fn->Call(env()->context(), v8::Null(env()->isolate()), 1, &uid);
if (ret.IsEmpty())
FatalError("node::AsyncWrap::~AsyncWrap", "destroy hook threw");
}
}
inline bool AsyncWrap::ran_init_callback() const {
return static_cast<bool>(bits_ & 1);
}

View File

@ -131,6 +131,8 @@ static void SetupHooks(const FunctionCallbackInfo<Value>& args) {
env->set_async_hooks_pre_function(args[1].As<Function>());
if (args[2]->IsFunction())
env->set_async_hooks_post_function(args[2].As<Function>());
if (args[3]->IsFunction())
env->set_async_hooks_destroy_function(args[3].As<Function>());
}
@ -156,6 +158,7 @@ static void Initialize(Local<Object> target,
env->set_async_hooks_init_function(Local<Function>());
env->set_async_hooks_pre_function(Local<Function>());
env->set_async_hooks_post_function(Local<Function>());
env->set_async_hooks_destroy_function(Local<Function>());
}

View File

@ -51,7 +51,7 @@ class AsyncWrap : public BaseObject {
ProviderType provider,
AsyncWrap* parent = nullptr);
inline virtual ~AsyncWrap() override = default;
inline virtual ~AsyncWrap();
inline ProviderType provider_type() const;

View File

@ -236,6 +236,7 @@ namespace node {
V(async_hooks_init_function, v8::Function) \
V(async_hooks_pre_function, v8::Function) \
V(async_hooks_post_function, v8::Function) \
V(async_hooks_destroy_function, v8::Function) \
V(binding_cache_object, v8::Object) \
V(buffer_constructor_function, v8::Function) \
V(buffer_prototype_object, v8::Object) \

View File

@ -10,7 +10,7 @@ let cntr = 0;
let server;
let client;
function init(type, parent) {
function init(type, id, parent) {
if (parent) {
cntr++;
// Cannot assert in init callback or will abort.

View File

@ -9,7 +9,7 @@ let cntr = 0;
let server;
let client;
function init(type, parent) {
function init(type, id, parent) {
if (parent) {
cntr++;
// Cannot assert in init callback or will abort.