#include "base-object-inl.h"
#include "env.h"
#include "env-inl.h"
+#include "node_internals.h"
#include "util.h"
#include "util-inl.h"
#include "v8.h"
ProviderType provider,
AsyncWrap* parent)
: 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())
+ return;
+
+ // TODO(trevnorris): Until it's verified all passed object's are not weak,
+ // add a HandleScope to make sure there's no leak.
+ v8::HandleScope scope(env->isolate());
+
+ v8::Local<v8::Object> parent_obj;
+
+ v8::TryCatch try_catch;
+
+ // If a parent value was sent then call its pre/post functions to let it know
+ // a conceptual "child" is being instantiated (e.g. that a server has
+ // received a connection).
+ if (parent != nullptr) {
+ parent_obj = parent->object();
+ env->async_hooks_pre_function()->Call(parent_obj, 0, nullptr);
+ if (try_catch.HasCaught())
+ 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");
+
+ has_async_queue_ = true;
+
+ if (parent != nullptr) {
+ env->async_hooks_post_function()->Call(parent_obj, 0, nullptr);
+ if (try_catch.HasCaught())
+ FatalError("node::AsyncWrap::AsyncWrap", "parent post hook threw");
+ }
}
using v8::Context;
using v8::Function;
+using v8::FunctionCallbackInfo;
using v8::Handle;
using v8::HandleScope;
using v8::Integer;
using v8::Object;
using v8::TryCatch;
using v8::Value;
+using v8::kExternalUint32Array;
namespace node {
+static void SetupHooks(const FunctionCallbackInfo<Value>& args) {
+ Environment* env = Environment::GetCurrent(args.GetIsolate());
+
+ CHECK(args[0]->IsObject());
+ CHECK(args[1]->IsFunction());
+ CHECK(args[2]->IsFunction());
+ CHECK(args[3]->IsFunction());
+
+ // Attach Fields enum from Environment::AsyncHooks.
+ // Flags attached to this object are:
+ // - kCallInitHook (0): Tells the AsyncWrap constructor whether it should
+ // make a call to the init JS callback. This is disabled by default, so
+ // even after setting the callbacks the flag will have to be set to
+ // 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.
+ Local<Object> async_hooks_obj = args[0].As<Object>();
+ Environment::AsyncHooks* async_hooks = env->async_hooks();
+ async_hooks_obj->SetIndexedPropertiesToExternalArrayData(
+ async_hooks->fields(),
+ kExternalUint32Array,
+ async_hooks->fields_count());
+
+ env->set_async_hooks_init_function(args[1].As<Function>());
+ env->set_async_hooks_pre_function(args[2].As<Function>());
+ env->set_async_hooks_post_function(args[3].As<Function>());
+}
+
+
static void Initialize(Handle<Object> target,
Handle<Value> unused,
Handle<Context> context) {
Isolate* isolate = env->isolate();
HandleScope scope(isolate);
+ NODE_SET_METHOD(target, "setupHooks", SetupHooks);
+
Local<Object> async_providers = Object::New(isolate);
#define V(PROVIDER) \
async_providers->Set(FIXED_ONE_BYTE_STRING(isolate, #PROVIDER), \
}
}
+ if (has_async_queue_) {
+ try_catch.SetVerbose(false);
+ env()->async_hooks_pre_function()->Call(context, 0, nullptr);
+ if (try_catch.HasCaught())
+ FatalError("node::AsyncWrap::MakeCallback", "pre hook threw");
+ try_catch.SetVerbose(true);
+ }
+
Local<Value> ret = cb->Call(context, argc, argv);
if (try_catch.HasCaught()) {
return Undefined(env()->isolate());
}
+ if (has_async_queue_) {
+ try_catch.SetVerbose(false);
+ env()->async_hooks_post_function()->Call(context, 0, nullptr);
+ if (try_catch.HasCaught())
+ FatalError("node::AsyncWrap::MakeCallback", "post hook threw");
+ try_catch.SetVerbose(true);
+ }
+
if (has_domain) {
Local<Value> exit_v = domain->Get(env()->exit_string());
if (exit_v->IsFunction()) {
private:
inline AsyncWrap();
- uint32_t provider_type_;
+ // When the async hooks init JS function is called from the constructor it is
+ // expected the context object will receive a _asyncQueue object property
+ // that will be used to call pre/post in MakeCallback.
+ bool has_async_queue_;
+ ProviderType provider_type_;
};
} // namespace node
return isolate_;
}
+inline Environment::AsyncHooks::AsyncHooks() {
+ for (int i = 0; i < kFieldsCount; i++) fields_[i] = 0;
+}
+
+inline uint32_t* Environment::AsyncHooks::fields() {
+ return fields_;
+}
+
+inline int Environment::AsyncHooks::fields_count() const {
+ return kFieldsCount;
+}
+
+inline bool Environment::AsyncHooks::call_init_hook() {
+ return fields_[kCallInitHook] != 0;
+}
+
inline Environment::DomainFlag::DomainFlag() {
for (int i = 0; i < kFieldsCount; ++i) fields_[i] = 0;
}
return isolate_;
}
+inline bool Environment::call_async_init_hook() const {
+ // The const_cast is okay, it doesn't violate conceptual const-ness.
+ return const_cast<Environment*>(this)->async_hooks()->call_init_hook();
+}
+
inline bool Environment::in_domain() const {
// The const_cast is okay, it doesn't violate conceptual const-ness.
return using_domains() &&
return isolate_data()->event_loop();
}
+inline Environment::AsyncHooks* Environment::async_hooks() {
+ return &async_hooks_;
+}
+
inline Environment::DomainFlag* Environment::domain_flag() {
return &domain_flag_;
}
V(args_string, "args") \
V(argv_string, "argv") \
V(async, "async") \
+ V(async_queue_string, "_asyncQueue") \
V(atime_string, "atime") \
V(birthtime_string, "birthtime") \
V(blksize_string, "blksize") \
V(zero_return_string, "ZERO_RETURN") \
#define ENVIRONMENT_STRONG_PERSISTENT_PROPERTIES(V) \
+ V(async_hooks_init_function, v8::Function) \
+ V(async_hooks_pre_function, v8::Function) \
+ V(async_hooks_post_function, v8::Function) \
V(binding_cache_object, v8::Object) \
V(buffer_constructor_function, v8::Function) \
V(context, v8::Context) \
class Environment {
public:
+ class AsyncHooks {
+ public:
+ inline uint32_t* fields();
+ inline int fields_count() const;
+ inline bool call_init_hook();
+
+ private:
+ friend class Environment; // So we can call the constructor.
+ inline AsyncHooks();
+
+ enum Fields {
+ // Set this to not zero if the init hook should be called.
+ kCallInitHook,
+ kFieldsCount
+ };
+
+ uint32_t fields_[kFieldsCount];
+
+ DISALLOW_COPY_AND_ASSIGN(AsyncHooks);
+ };
+
class DomainFlag {
public:
inline uint32_t* fields();
inline v8::Isolate* isolate() const;
inline uv_loop_t* event_loop() const;
+ inline bool call_async_init_hook() const;
inline bool in_domain() const;
inline uint32_t watched_providers() const;
void *arg);
inline void FinishHandleCleanup(uv_handle_t* handle);
+ inline AsyncHooks* async_hooks();
inline DomainFlag* domain_flag();
inline TickInfo* tick_info();
uv_idle_t immediate_idle_handle_;
uv_prepare_t idle_prepare_handle_;
uv_check_t idle_check_handle_;
+ AsyncHooks async_hooks_;
DomainFlag domain_flag_;
TickInfo tick_info_;
uv_timer_t cares_timer_handle_;
Local<Object> process = env->process_object();
Local<Object> object, domain;
+ bool has_async_queue = false;
bool has_domain = false;
+ if (recv->IsObject()) {
+ object = recv.As<Object>();
+ Local<Value> async_queue_v = object->Get(env->async_queue_string());
+ if (async_queue_v->IsObject())
+ has_async_queue = true;
+ }
+
if (env->using_domains()) {
CHECK(recv->IsObject());
- object = recv.As<Object>();
Local<Value> domain_v = object->Get(env->domain_string());
has_domain = domain_v->IsObject();
if (has_domain) {
}
}
+ if (has_async_queue) {
+ try_catch.SetVerbose(false);
+ env->async_hooks_pre_function()->Call(object, 0, nullptr);
+ if (try_catch.HasCaught())
+ FatalError("node:;MakeCallback", "pre hook threw");
+ try_catch.SetVerbose(true);
+ }
+
Local<Value> ret = callback->Call(recv, argc, argv);
+ if (has_async_queue) {
+ try_catch.SetVerbose(false);
+ env->async_hooks_post_function()->Call(object, 0, nullptr);
+ if (try_catch.HasCaught())
+ FatalError("node::MakeCallback", "post hook threw");
+ try_catch.SetVerbose(true);
+ }
+
if (has_domain) {
Local<Value> exit_v = domain->Get(env->exit_string());
if (exit_v->IsFunction()) {