diff options
author | Trevor Norris <trev.norris@gmail.com> | 2014-01-09 11:11:40 -0800 |
---|---|---|
committer | Trevor Norris <trev.norris@gmail.com> | 2014-01-09 13:25:20 -0800 |
commit | 828f14556e0daeae7fdac08fceaa90952de63f73 (patch) | |
tree | 2eb2108d84e1bfc88b452970c3b04d00166e6ed0 /src | |
parent | 0afdfae0eb1ab263337889e716d5e8fe5e54c453 (diff) | |
download | android-node-v8-828f14556e0daeae7fdac08fceaa90952de63f73.tar.gz android-node-v8-828f14556e0daeae7fdac08fceaa90952de63f73.tar.bz2 android-node-v8-828f14556e0daeae7fdac08fceaa90952de63f73.zip |
src: revert domain using AsyncListeners
This is a slightly modified revert of bc39bdd.
Getting domains to use AsyncListeners became too much of a challenge
with many edge cases. While this is still a goal, it will have to be
deferred for now until more test coverage can be provided.
Diffstat (limited to 'src')
-rw-r--r-- | src/async-wrap-inl.h | 92 | ||||
-rw-r--r-- | src/async-wrap.h | 7 | ||||
-rw-r--r-- | src/env-inl.h | 35 | ||||
-rw-r--r-- | src/env.h | 29 | ||||
-rw-r--r-- | src/node.cc | 162 | ||||
-rw-r--r-- | src/node.js | 39 | ||||
-rw-r--r-- | src/node_crypto.cc | 6 | ||||
-rw-r--r-- | src/req_wrap.h | 3 |
8 files changed, 370 insertions, 3 deletions
diff --git a/src/async-wrap-inl.h b/src/async-wrap-inl.h index 58c2d78b2a..b32ead8030 100644 --- a/src/async-wrap-inl.h +++ b/src/async-wrap-inl.h @@ -88,10 +88,102 @@ inline bool AsyncWrap::has_async_queue() { } +// I hate you domains. +inline v8::Handle<v8::Value> AsyncWrap::MakeDomainCallback( + const v8::Handle<v8::Function> cb, + int argc, + v8::Handle<v8::Value>* argv) { + assert(env()->context() == env()->isolate()->GetCurrentContext()); + + v8::Local<v8::Object> context = object(); + v8::Local<v8::Object> process = env()->process_object(); + v8::Local<v8::Value> domain_v = context->Get(env()->domain_string()); + v8::Local<v8::Object> domain; + + v8::TryCatch try_catch; + try_catch.SetVerbose(true); + + if (has_async_queue()) { + v8::Local<v8::Value> val = context.As<v8::Value>(); + env()->async_listener_load_function()->Call(process, 1, &val); + + if (try_catch.HasCaught()) + return v8::Undefined(env()->isolate()); + } + + bool has_domain = domain_v->IsObject(); + if (has_domain) { + domain = domain_v.As<v8::Object>(); + + if (domain->Get(env()->disposed_string())->IsTrue()) + return Undefined(env()->isolate()); + + v8::Local<v8::Function> enter = + domain->Get(env()->enter_string()).As<v8::Function>(); + assert(enter->IsFunction()); + enter->Call(domain, 0, NULL); + + if (try_catch.HasCaught()) + return Undefined(env()->isolate()); + } + + v8::Local<v8::Value> ret = cb->Call(context, argc, argv); + + if (try_catch.HasCaught()) { + return Undefined(env()->isolate()); + } + + if (has_domain) { + v8::Local<v8::Function> exit = + domain->Get(env()->exit_string()).As<v8::Function>(); + assert(exit->IsFunction()); + exit->Call(domain, 0, NULL); + + if (try_catch.HasCaught()) + return Undefined(env()->isolate()); + } + + if (has_async_queue()) { + v8::Local<v8::Value> val = context.As<v8::Value>(); + env()->async_listener_unload_function()->Call(process, 1, &val); + + if (try_catch.HasCaught()) + return Undefined(env()->isolate()); + } + + Environment::TickInfo* tick_info = env()->tick_info(); + + if (tick_info->in_tick()) { + return ret; + } + + if (tick_info->length() == 0) { + tick_info->set_index(0); + return ret; + } + + tick_info->set_in_tick(true); + + env()->tick_callback_function()->Call(process, 0, NULL); + + tick_info->set_in_tick(false); + + if (try_catch.HasCaught()) { + tick_info->set_last_threw(true); + return Undefined(env()->isolate()); + } + + return ret; +} + + inline v8::Handle<v8::Value> AsyncWrap::MakeCallback( const v8::Handle<v8::Function> cb, int argc, v8::Handle<v8::Value>* argv) { + if (env()->using_domains()) + return MakeDomainCallback(cb, argc, argv); + assert(env()->context() == env()->isolate()->GetCurrentContext()); v8::Local<v8::Object> context = object(); diff --git a/src/async-wrap.h b/src/async-wrap.h index b978ae3c5b..25ee2eccd2 100644 --- a/src/async-wrap.h +++ b/src/async-wrap.h @@ -62,6 +62,13 @@ class AsyncWrap : public BaseObject { v8::Handle<v8::Value>* argv); private: + // TODO(trevnorris): BURN IN FIRE! Remove this as soon as a suitable + // replacement is committed. + inline v8::Handle<v8::Value> MakeDomainCallback( + const v8::Handle<v8::Function> cb, + int argc, + v8::Handle<v8::Value>* argv); + // Add an async listener to an existing handle. template <typename Type> static inline void AddAsyncListener( diff --git a/src/env-inl.h b/src/env-inl.h index 4578f6ebc0..c7baff4052 100644 --- a/src/env-inl.h +++ b/src/env-inl.h @@ -86,6 +86,22 @@ inline uint32_t Environment::AsyncListener::count() const { return fields_[kCount]; } +inline Environment::DomainFlag::DomainFlag() { + for (int i = 0; i < kFieldsCount; ++i) fields_[i] = 0; +} + +inline uint32_t* Environment::DomainFlag::fields() { + return fields_; +} + +inline int Environment::DomainFlag::fields_count() const { + return kFieldsCount; +} + +inline uint32_t Environment::DomainFlag::count() const { + return fields_[kCount]; +} + inline Environment::TickInfo::TickInfo() : in_tick_(false), last_threw_(false) { for (int i = 0; i < kFieldsCount; ++i) fields_[i] = 0; @@ -163,6 +179,7 @@ inline Environment::Environment(v8::Local<v8::Context> context) : isolate_(context->GetIsolate()), isolate_data_(IsolateData::GetOrCreate(context->GetIsolate())), using_smalloc_alloc_cb_(false), + using_domains_(false), context_(context->GetIsolate(), context) { // We'll be creating new objects so make sure we've entered the context. v8::HandleScope handle_scope(isolate()); @@ -193,6 +210,12 @@ inline bool Environment::has_async_listeners() const { return const_cast<Environment*>(this)->async_listener()->count() > 0; } +inline bool Environment::in_domain() const { + // The const_cast is okay, it doesn't violate conceptual const-ness. + return using_domains() && + const_cast<Environment*>(this)->domain_flag()->count() > 0; +} + inline Environment* Environment::from_immediate_check_handle( uv_check_t* handle) { return CONTAINER_OF(handle, Environment, immediate_check_handle_); @@ -231,6 +254,10 @@ inline Environment::AsyncListener* Environment::async_listener() { return &async_listener_count_; } +inline Environment::DomainFlag* Environment::domain_flag() { + return &domain_flag_; +} + inline Environment::TickInfo* Environment::tick_info() { return &tick_info_; } @@ -243,6 +270,14 @@ inline void Environment::set_using_smalloc_alloc_cb(bool value) { using_smalloc_alloc_cb_ = value; } +inline bool Environment::using_domains() const { + return using_domains_; +} + +inline void Environment::set_using_domains(bool value) { + using_domains_ = value; +} + inline Environment* Environment::from_cares_timer_handle(uv_timer_t* handle) { return CONTAINER_OF(handle, Environment, cares_timer_handle_); } @@ -66,6 +66,7 @@ namespace node { V(ctime_string, "ctime") \ V(dev_string, "dev") \ V(disposed_string, "_disposed") \ + V(domain_string, "domain") \ V(enter_string, "enter") \ V(errno_string, "errno") \ V(exit_string, "exit") \ @@ -143,6 +144,7 @@ namespace node { V(binding_cache_object, v8::Object) \ V(buffer_constructor_function, v8::Function) \ V(context, v8::Context) \ + V(domain_array, v8::Array) \ V(module_load_list_array, v8::Array) \ V(pipe_constructor_template, v8::FunctionTemplate) \ V(process_object, v8::Object) \ @@ -191,6 +193,26 @@ class Environment { DISALLOW_COPY_AND_ASSIGN(AsyncListener); }; + class DomainFlag { + public: + inline uint32_t* fields(); + inline int fields_count() const; + inline uint32_t count() const; + + private: + friend class Environment; // So we can call the constructor. + inline DomainFlag(); + + enum Fields { + kCount, + kFieldsCount + }; + + uint32_t fields_[kFieldsCount]; + + DISALLOW_COPY_AND_ASSIGN(DomainFlag); + }; + class TickInfo { public: inline uint32_t* fields(); @@ -232,6 +254,7 @@ class Environment { inline v8::Isolate* isolate() const; inline uv_loop_t* event_loop() const; inline bool has_async_listeners() const; + inline bool in_domain() const; static inline Environment* from_immediate_check_handle(uv_check_t* handle); inline uv_check_t* immediate_check_handle(); @@ -244,6 +267,7 @@ class Environment { inline uv_check_t* idle_check_handle(); inline AsyncListener* async_listener(); + inline DomainFlag* domain_flag(); inline TickInfo* tick_info(); static inline Environment* from_cares_timer_handle(uv_timer_t* handle); @@ -255,6 +279,9 @@ class Environment { inline bool using_smalloc_alloc_cb() const; inline void set_using_smalloc_alloc_cb(bool value); + inline bool using_domains() const; + inline void set_using_domains(bool value); + // Strings are shared across shared contexts. The getters simply proxy to // the per-isolate primitive. #define V(PropertyName, StringValue) \ @@ -285,11 +312,13 @@ class Environment { uv_prepare_t idle_prepare_handle_; uv_check_t idle_check_handle_; AsyncListener async_listener_count_; + DomainFlag domain_flag_; TickInfo tick_info_; uv_timer_t cares_timer_handle_; ares_channel cares_channel_; ares_task_list cares_task_list_; bool using_smalloc_alloc_cb_; + bool using_domains_; #define V(PropertyName, TypeName) \ v8::Persistent<TypeName> PropertyName ## _; diff --git a/src/node.cc b/src/node.cc index dd370ad401..a39459cf63 100644 --- a/src/node.cc +++ b/src/node.cc @@ -876,11 +876,54 @@ void SetupAsyncListener(const FunctionCallbackInfo<Value>& args) { } +void SetupDomainUse(const FunctionCallbackInfo<Value>& args) { + Environment* env = Environment::GetCurrent(args.GetIsolate()); + + if (env->using_domains()) + return; + env->set_using_domains(true); + + HandleScope scope(node_isolate); + Local<Object> process_object = env->process_object(); + + Local<String> tick_callback_function_key = + FIXED_ONE_BYTE_STRING(node_isolate, "_tickDomainCallback"); + Local<Function> tick_callback_function = + process_object->Get(tick_callback_function_key).As<Function>(); + + if (!tick_callback_function->IsFunction()) { + fprintf(stderr, "process._tickDomainCallback assigned to non-function\n"); + abort(); + } + + process_object->Set(FIXED_ONE_BYTE_STRING(node_isolate, "_tickCallback"), + tick_callback_function); + env->set_tick_callback_function(tick_callback_function); + + assert(args[0]->IsArray()); + assert(args[1]->IsObject()); + + env->set_domain_array(args[0].As<Array>()); + + Local<Object> domain_flag_obj = args[1].As<Object>(); + Environment::DomainFlag* domain_flag = env->domain_flag(); + domain_flag_obj->SetIndexedPropertiesToExternalArrayData( + domain_flag->fields(), + kExternalUnsignedIntArray, + domain_flag->fields_count()); + + // Do a little housekeeping. + env->process_object()->Delete( + FIXED_ONE_BYTE_STRING(args.GetIsolate(), "_setupDomainUse")); +} + + void SetupNextTick(const FunctionCallbackInfo<Value>& args) { HandleScope handle_scope(args.GetIsolate()); Environment* env = Environment::GetCurrent(args.GetIsolate()); - assert(args[0]->IsObject() && args[1]->IsFunction()); + assert(args[0]->IsObject()); + assert(args[1]->IsFunction()); // Values use to cross communicate with processNextTick. Local<Object> tick_info_obj = args[0].As<Object>(); @@ -897,11 +940,114 @@ void SetupNextTick(const FunctionCallbackInfo<Value>& args) { } +Handle<Value> MakeDomainCallback(Environment* env, + Handle<Object> object, + const Handle<Function> callback, + int argc, + Handle<Value> argv[]) { + // If you hit this assertion, you forgot to enter the v8::Context first. + assert(env->context() == env->isolate()->GetCurrentContext()); + + Local<Object> process = env->process_object(); + Local<Value> domain_v = object->Get(env->domain_string()); + Local<Object> domain; + + TryCatch try_catch; + try_catch.SetVerbose(true); + + // TODO(trevnorris): This is sucky for performance. Fix it. + bool has_async_queue = object->Has(env->async_queue_string()); + if (has_async_queue) { + Local<Value> argv[] = { object }; + env->async_listener_load_function()->Call(process, ARRAY_SIZE(argv), argv); + + if (try_catch.HasCaught()) + return Undefined(node_isolate); + } + + bool has_domain = domain_v->IsObject(); + if (has_domain) { + domain = domain_v.As<Object>(); + + if (domain->Get(env->disposed_string())->IsTrue()) { + // domain has been disposed of. + return Undefined(node_isolate); + } + + Local<Function> enter = + domain->Get(env->enter_string()).As<Function>(); + assert(enter->IsFunction()); + enter->Call(domain, 0, NULL); + + if (try_catch.HasCaught()) { + return Undefined(node_isolate); + } + } + + Local<Value> ret = callback->Call(object, argc, argv); + + if (try_catch.HasCaught()) { + return Undefined(node_isolate); + } + + if (has_domain) { + Local<Function> exit = + domain->Get(env->exit_string()).As<Function>(); + assert(exit->IsFunction()); + exit->Call(domain, 0, NULL); + + if (try_catch.HasCaught()) { + return Undefined(node_isolate); + } + } + + if (has_async_queue) { + Local<Value> val = object.As<Value>(); + env->async_listener_unload_function()->Call(process, 1, &val); + + if (try_catch.HasCaught()) + return Undefined(node_isolate); + } + + Environment::TickInfo* tick_info = env->tick_info(); + + if (tick_info->last_threw() == 1) { + tick_info->set_last_threw(0); + return ret; + } + + if (tick_info->in_tick()) { + return ret; + } + + if (tick_info->length() == 0) { + tick_info->set_index(0); + return ret; + } + + tick_info->set_in_tick(true); + + env->tick_callback_function()->Call(process, 0, NULL); + + tick_info->set_in_tick(false); + + if (try_catch.HasCaught()) { + tick_info->set_last_threw(true); + return Undefined(node_isolate); + } + + return ret; +} + + Handle<Value> MakeCallback(Environment* env, Handle<Object> object, const Handle<Function> callback, int argc, Handle<Value> argv[]) { + if (env->using_domains()) + return MakeDomainCallback(env, object, callback, argc, argv); + // If you hit this assertion, you forgot to enter the v8::Context first. assert(env->context() == env->isolate()->GetCurrentContext()); @@ -1031,6 +1177,19 @@ Handle<Value> MakeCallback(const Handle<Object> object, } +Handle<Value> MakeDomainCallback(const Handle<Object> object, + const Handle<Function> callback, + int argc, + Handle<Value> argv[]) { + Local<Context> context = object->CreationContext(); + Environment* env = Environment::GetCurrent(context); + Context::Scope context_scope(context); + HandleScope handle_scope(env->isolate()); + return handle_scope.Close( + MakeDomainCallback(env, object, callback, argc, argv)); +} + + enum encoding ParseEncoding(Handle<Value> encoding_v, enum encoding _default) { HandleScope scope(node_isolate); @@ -2482,6 +2641,7 @@ void SetupProcessObject(Environment* env, NODE_SET_METHOD(process, "_setupAsyncListener", SetupAsyncListener); NODE_SET_METHOD(process, "_setupNextTick", SetupNextTick); + NODE_SET_METHOD(process, "_setupDomainUse", SetupDomainUse); // values use to cross communicate with processNextTick Local<Object> tick_info_obj = Object::New(); diff --git a/src/node.js b/src/node.js index 1c0fc8b59c..2c61592737 100644 --- a/src/node.js +++ b/src/node.js @@ -224,11 +224,14 @@ // First run through error handlers from asyncListener. var caught = _errorHandler(er); + if (process.domain && process.domain._errorHandler) + caught = process.domain._errorHandler(er) || caught; + if (!caught) caught = process.emit('uncaughtException', er); - // If someone handled it, then great. Otherwise die in C++ since - // that means we'll exit the process, emit the 'exit' event. + // If someone handled it, then great. otherwise, die in C++ land + // since that means that we'll exit the process, emit the 'exit' event if (!caught) { try { if (!process._exiting) { @@ -568,6 +571,7 @@ process.nextTick = nextTick; // Needs to be accessible from beyond this scope. process._tickCallback = _tickCallback; + process._tickDomainCallback = _tickDomainCallback; process._setupNextTick(tickInfo, _tickCallback); @@ -585,6 +589,7 @@ } // Run callbacks that have no domain. + // Using domains will cause this to be overridden. function _tickCallback() { var callback, hasQueue, threw, tock; @@ -611,6 +616,35 @@ tickDone(); } + function _tickDomainCallback() { + var callback, domain, hasQueue, threw, tock; + + while (tickInfo[kIndex] < tickInfo[kLength]) { + tock = nextTickQueue[tickInfo[kIndex]++]; + callback = tock.callback; + domain = tock.domain; + hasQueue = !!tock._asyncQueue; + if (hasQueue) + _loadAsyncQueue(tock); + if (domain) + domain.enter(); + threw = true; + try { + callback(); + threw = false; + } finally { + if (threw) + tickDone(); + } + if (hasQueue) + _unloadAsyncQueue(tock); + if (domain) + domain.exit(); + } + + tickDone(); + } + function nextTick(callback) { // on the way out, don't bother. it won't get fired anyway. if (process._exiting) @@ -618,6 +652,7 @@ var obj = { callback: callback, + domain: process.domain || null, _asyncQueue: undefined }; diff --git a/src/node_crypto.cc b/src/node_crypto.cc index e1eae7f2fd..6b6beb40bb 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -3628,6 +3628,9 @@ void PBKDF2(const FunctionCallbackInfo<Value>& args) { if (args[4]->IsFunction()) { obj->Set(env->ondone_string(), args[4]); + // XXX(trevnorris): This will need to go with the rest of domains. + if (env->in_domain()) + obj->Set(env->domain_string(), env->domain_array()->Get(0)); uv_queue_work(env->event_loop(), req->work_req(), EIO_PBKDF2, @@ -3789,6 +3792,9 @@ void RandomBytes(const FunctionCallbackInfo<Value>& args) { if (args[1]->IsFunction()) { obj->Set(FIXED_ONE_BYTE_STRING(node_isolate, "ondone"), args[1]); + // XXX(trevnorris): This will need to go with the rest of domains. + if (env->in_domain()) + obj->Set(env->domain_string(), env->domain_array()->Get(0)); uv_queue_work(env->event_loop(), req->work_req(), RandomBytesWork<pseudoRandom>, diff --git a/src/req_wrap.h b/src/req_wrap.h index 2e08ef7380..043edb2f60 100644 --- a/src/req_wrap.h +++ b/src/req_wrap.h @@ -39,6 +39,9 @@ class ReqWrap : public AsyncWrap { public: ReqWrap(Environment* env, v8::Handle<v8::Object> object) : AsyncWrap(env, object) { + if (env->in_domain()) + object->Set(env->domain_string(), env->domain_array()->Get(0)); + QUEUE_INSERT_TAIL(&req_wrap_queue, &req_wrap_queue_); } |