summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTrevor Norris <trev.norris@gmail.com>2014-01-09 11:11:40 -0800
committerTrevor Norris <trev.norris@gmail.com>2014-01-09 13:25:20 -0800
commit828f14556e0daeae7fdac08fceaa90952de63f73 (patch)
tree2eb2108d84e1bfc88b452970c3b04d00166e6ed0 /src
parent0afdfae0eb1ab263337889e716d5e8fe5e54c453 (diff)
downloadandroid-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.h92
-rw-r--r--src/async-wrap.h7
-rw-r--r--src/env-inl.h35
-rw-r--r--src/env.h29
-rw-r--r--src/node.cc162
-rw-r--r--src/node.js39
-rw-r--r--src/node_crypto.cc6
-rw-r--r--src/req_wrap.h3
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_);
}
diff --git a/src/env.h b/src/env.h
index 6db4a08c2a..71936dd5c2 100644
--- a/src/env.h
+++ b/src/env.h
@@ -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_);
}