diff --git a/src/async-wrap-inl.h b/src/async-wrap-inl.h index 647a381e88f7d2..088ca5871598ec 100644 --- a/src/async-wrap-inl.h +++ b/src/async-wrap-inl.h @@ -20,8 +20,8 @@ inline AsyncWrap::AsyncWrap(Environment* env, : BaseObject(env, object), has_async_queue_(false), provider_type_(provider) { - // Check user controlled flag to see if the init callback should run. - if (!env->call_async_init_hook()) + // Check user controlled flag to see if the async hooks should be called. + if (!env->use_async_hook()) return; // TODO(trevnorris): Until it's verified all passed object's are not weak, @@ -42,11 +42,13 @@ inline AsyncWrap::AsyncWrap(Environment* env, FatalError("node::AsyncWrap::AsyncWrap", "parent pre hook threw"); } - env->async_hooks_init_function()->Call(object, 0, nullptr); - - if (try_catch.HasCaught()) - FatalError("node::AsyncWrap::AsyncWrap", "init hook threw"); + // Check user controlled flag to see if the init callback should run. + if (env->call_async_init_hook()) { + env->async_hooks_init_function()->Call(object, 0, nullptr); + if (try_catch.HasCaught()) + FatalError("node::AsyncWrap::AsyncWrap", "init hook threw"); + } has_async_queue_ = true; if (parent != nullptr) { diff --git a/src/async-wrap.cc b/src/async-wrap.cc index eee020b788aa2f..ebd92b7ad679bb 100644 --- a/src/async-wrap.cc +++ b/src/async-wrap.cc @@ -38,6 +38,8 @@ static void SetupHooks(const FunctionCallbackInfo& args) { // non-zero to have those callbacks called. This only affects the init // callback. If the init callback was called, then the pre/post callbacks // will automatically be called. + // - kEnabled (1): Tells the AsyncWrap constructor whether it should + // make any calls to the JS hook functions. Local async_hooks_obj = args[0].As(); Environment::AsyncHooks* async_hooks = env->async_hooks(); async_hooks_obj->SetIndexedPropertiesToExternalArrayData( @@ -45,6 +47,8 @@ static void SetupHooks(const FunctionCallbackInfo& args) { kExternalUint32Array, async_hooks->fields_count()); + async_hooks->set_enabled(true); + env->set_async_hooks_init_function(args[1].As()); env->set_async_hooks_pre_function(args[2].As()); env->set_async_hooks_post_function(args[3].As()); diff --git a/src/env-inl.h b/src/env-inl.h index c5f2328e1259ea..c0a7e024ea347d 100644 --- a/src/env-inl.h +++ b/src/env-inl.h @@ -70,6 +70,14 @@ inline bool Environment::AsyncHooks::call_init_hook() { return fields_[kCallInitHook] != 0; } +inline bool Environment::AsyncHooks::is_enabled() const { + return fields_[kEnabled] != 0; +} + +inline void Environment::AsyncHooks::set_enabled(bool enabled) { + fields_[kEnabled] = static_cast(enabled); +} + inline Environment::DomainFlag::DomainFlag() { for (int i = 0; i < kFieldsCount; ++i) fields_[i] = 0; } @@ -214,6 +222,11 @@ inline v8::Isolate* Environment::isolate() const { return isolate_; } +inline bool Environment::use_async_hook() const { + // The const_cast is okay, it doesn't violate conceptual const-ness. + return const_cast(this)->async_hooks()->is_enabled(); +} + inline bool Environment::call_async_init_hook() const { // The const_cast is okay, it doesn't violate conceptual const-ness. return const_cast(this)->async_hooks()->call_init_hook(); diff --git a/src/env.h b/src/env.h index 24bb0905bc3f14..268885643bb576 100644 --- a/src/env.h +++ b/src/env.h @@ -255,6 +255,8 @@ class Environment { inline uint32_t* fields(); inline int fields_count() const; inline bool call_init_hook(); + inline bool is_enabled() const; + inline void set_enabled(bool enabled); private: friend class Environment; // So we can call the constructor. @@ -263,6 +265,9 @@ class Environment { enum Fields { // Set this to not zero if the init hook should be called. kCallInitHook, + // Set this to not zero if async wrap should be enabled. This is + // automatically set when SetupHooks is called. + kEnabled, kFieldsCount }; @@ -358,6 +363,7 @@ class Environment { inline v8::Isolate* isolate() const; inline uv_loop_t* event_loop() const; + inline bool use_async_hook() const; inline bool call_async_init_hook() const; inline bool in_domain() const; inline uint32_t watched_providers() const; diff --git a/test/parallel/test-async-wrap-disabled.js b/test/parallel/test-async-wrap-disabled.js new file mode 100644 index 00000000000000..1fb62ae18226d4 --- /dev/null +++ b/test/parallel/test-async-wrap-disabled.js @@ -0,0 +1,33 @@ +var common = require('../common'); +var assert = require('assert'); + +var asyncWrap = process.binding('async_wrap'); + +var asyncHooksObject = {}; +var kEnabled = 1; +asyncWrap.setupHooks(asyncHooksObject, init, before, after); +asyncHooksObject[kEnabled] = 0; + +var order = []; + +function init() { + order.push('init ' + this.constructor.name); +} + +function before() { + order.push('before ' + this.constructor.name); +} + +function after() { + order.push('after ' + this.constructor.name); +} + +setTimeout(function () { + order.push('callback'); +}); + +process.once('exit', function () { + assert.deepEqual(order, [ + 'callback' + ]); +}); diff --git a/test/parallel/test-async-wrap-init-hook.js b/test/parallel/test-async-wrap-init-hook.js new file mode 100644 index 00000000000000..74c6c1bd79919e --- /dev/null +++ b/test/parallel/test-async-wrap-init-hook.js @@ -0,0 +1,33 @@ +var common = require('../common'); +var assert = require('assert'); + +var asyncWrap = process.binding('async_wrap'); + +var asyncHooksObject = {}; +var kCallInitHook = 0; +asyncWrap.setupHooks(asyncHooksObject, init, before, after); +asyncHooksObject[kCallInitHook] = 1; + +var order = []; + +function init() { + order.push('init ' + this.constructor.name); +} + +function before() { + order.push('before ' + this.constructor.name); +} + +function after() { + order.push('after ' + this.constructor.name); +} + +setTimeout(function () { + order.push('callback'); +}); + +process.once('exit', function () { + assert.deepEqual(order, [ + 'init Timer', 'before Timer', 'callback', 'after Timer' + ]); +}); diff --git a/test/parallel/test-async-wrap-no-init-hook.js b/test/parallel/test-async-wrap-no-init-hook.js new file mode 100644 index 00000000000000..0119d3e3c02456 --- /dev/null +++ b/test/parallel/test-async-wrap-no-init-hook.js @@ -0,0 +1,31 @@ +var common = require('../common'); +var assert = require('assert'); + +var asyncWrap = process.binding('async_wrap'); + +var asyncHooksObject = {}; +asyncWrap.setupHooks(asyncHooksObject, init, before, after); + +var order = []; + +function init() { + order.push('init ' + this.constructor.name); +} + +function before() { + order.push('before ' + this.constructor.name); +} + +function after() { + order.push('after ' + this.constructor.name); +} + +setTimeout(function () { + order.push('callback'); +}); + +process.once('exit', function () { + assert.deepEqual(order, [ + 'before Timer', 'callback', 'after Timer' + ]); +});