Browse Source

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>
process-exit-stdio-flushing
Trevor Norris 9 years ago
parent
commit
bb1bd76395
  1. 19
      src/async-wrap-inl.h
  2. 3
      src/async-wrap.cc
  3. 2
      src/async-wrap.h
  4. 1
      src/env.h
  5. 2
      test/parallel/test-async-wrap-disabled-propagate-parent.js
  6. 2
      test/parallel/test-async-wrap-propagate-parent.js

19
src/async-wrap-inl.h

@ -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);
}

3
src/async-wrap.cc

@ -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>());
}

2
src/async-wrap.h

@ -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;

1
src/env.h

@ -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) \

2
test/parallel/test-async-wrap-disabled-propagate-parent.js

@ -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.

2
test/parallel/test-async-wrap-propagate-parent.js

@ -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.

Loading…
Cancel
Save