diff --git a/lib/internal/async_wrap.js b/lib/internal/async_wrap.js new file mode 100644 index 00000000000000..b9d78ce281724e --- /dev/null +++ b/lib/internal/async_wrap.js @@ -0,0 +1,47 @@ +'use strict'; + +const async_wrap = process.binding('async_wrap'); + +const nextIdArray = async_wrap.getNextAsyncId2(); +const currentIdArray = async_wrap.getCurrentAsyncId2(); + +function getLittleEndian(a) { + if (a[0] === 0xffffffff) { + a[0] = 0; + a[1]++; + } + return a[0] + a[1] * 0x100000000; +} + +function getBigEndian(a) { + if (a[1] === 0xffffffff) { + a[1] = 0; + a[0]++; + } + return a[1] + a[0] * 0x100000000; +} + +function getCurrentIdLittleEndian() { + return getLittleEndian(currentIdArray); +} + +function getCurrentIdBigEndian() { + return getBigEndian(currentIdArray); +} + +function getNextIdLittleEndian() { + return getLittleEndian(nextIdArray); +} + +function getNextIdBigEndian() { + return getBigEndian(nextIdArray); +} + +module.exports.getCurrentAsyncWrapId = async_wrap.getCurrentAsyncId; +module.exports.getNextAsyncWrapId = async_wrap.getNextAsyncId; + +module.exports.getCurrentAsyncWrapId2 = + process.binding('os').isBigEndian ? getCurrentIdBigEndian : getCurrentIdLittleEndian; + +module.exports.getNextAsyncWrapId2 = + process.binding('os').isBigEndian ? getNextIdBigEndian : getNextIdLittleEndian; diff --git a/lib/internal/process/next_tick.js b/lib/internal/process/next_tick.js index 8e5502d9726ee8..f2284eceae9aab 100644 --- a/lib/internal/process/next_tick.js +++ b/lib/internal/process/next_tick.js @@ -5,6 +5,8 @@ exports.setup = setupNextTick; function setupNextTick() { var asyncWrap = process.binding('async_wrap'); + const async_wrap = require('internal/async_wrap'); + const promises = require('internal/process/promises'); const emitPendingUnhandledRejections = promises.setup(scheduleMicrotasks); var nextTickQueue = []; @@ -18,6 +20,7 @@ function setupNextTick() { var kLength = 1; process.nextTick = nextTick; + process.nextTickTemp = nextTickTemp; // Needs to be accessible from beyond this scope. process._tickCallback = _tickCallback; process._tickDomainCallback = _tickDomainCallback; @@ -152,14 +155,26 @@ function setupNextTick() { this.callback = c; this.domain = process.domain || null; this.args = args; - this.parentAsyncId = asyncWrap.getCurrentAsyncId(); - this.asyncId = asyncWrap.getNextAsyncId(); + this.parentAsyncId = async_wrap.getCurrentAsyncWrapId(); + this.asyncId = async_wrap.getNextAsyncWrapId(); this.asyncState = {}; this.ranInitCallback = asyncWrap.notifyAsyncEnqueue( this.asyncId, this.asyncState, undefined, undefined, asyncWrap.Providers.NEXTTICK); } + function TickObjectTemp(c, args) { + this.callback = c; + this.domain = process.domain || null; + this.args = args; + this.parentAsyncId = async_wrap.getCurrentAsyncWrapId2(); + this.asyncId = async_wrap.getNextAsyncWrapId2(); + this.asyncState = {}; + this.ranInitCallback = asyncWrap.notifyAsyncEnqueue( + this.asyncId, this.asyncState, undefined, undefined, + asyncWrap.Providers.NEXTTICK); + } + function nextTick(callback) { if (typeof callback !== 'function') throw new TypeError('callback is not a function'); @@ -177,4 +192,22 @@ function setupNextTick() { nextTickQueue.push(new TickObject(callback, args)); tickInfo[kLength]++; } + + function nextTickTemp(callback) { + if (typeof callback !== 'function') + throw new TypeError('callback is not a function'); + // on the way out, don't bother. it won't get fired anyway. + if (process._exiting) + return; + + var args; + if (arguments.length > 1) { + args = new Array(arguments.length - 1); + for (var i = 1; i < arguments.length; i++) + args[i - 1] = arguments[i]; + } + + nextTickQueue.push(new TickObjectTemp(callback, args)); + tickInfo[kLength]++; + } } diff --git a/node.gyp b/node.gyp index 0e9fe40c419af6..30acab1622e0ae 100644 --- a/node.gyp +++ b/node.gyp @@ -70,6 +70,7 @@ 'lib/v8.js', 'lib/vm.js', 'lib/zlib.js', + 'lib/internal/async_wrap.js', 'lib/internal/child_process.js', 'lib/internal/cluster.js', 'lib/internal/freelist.js', diff --git a/src/async-wrap-inl.h b/src/async-wrap-inl.h index 7a7bfb66b14141..8f7e1403d5e9f4 100644 --- a/src/async-wrap-inl.h +++ b/src/async-wrap-inl.h @@ -18,7 +18,7 @@ inline AsyncWrap::AsyncWrap(Environment* env, ProviderType provider, AsyncWrap* parent) : BaseObject(env, object), bits_(static_cast(provider) << 1), - uid_(env->get_next_async_wrap_uid()) { + uid_(env->async_hooks()->get_next_async_wrap_uid()) { CHECK_NE(provider, PROVIDER_NONE); CHECK_GE(object->InternalFieldCount(), 1); diff --git a/src/async-wrap.cc b/src/async-wrap.cc index ad9ba1894f2e09..99cf3086ce527e 100644 --- a/src/async-wrap.cc +++ b/src/async-wrap.cc @@ -120,16 +120,26 @@ static void DisableHooksJS(const FunctionCallbackInfo& args) { static void GetCurrentAsyncId(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); - v8::Local uid = v8::Integer::New(env->isolate(), env->get_current_async_wrap_uid()); + v8::Local uid = v8::Integer::New(env->isolate(), env->async_hooks()->get_current_async_wrap_uid()); args.GetReturnValue().Set(uid); } static void GetNextAsyncId(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); - v8::Local uid = v8::Integer::New(env->isolate(), env->get_next_async_wrap_uid()); + v8::Local uid = v8::Integer::New(env->isolate(), env->async_hooks()->get_next_async_wrap_uid()); args.GetReturnValue().Set(uid); } +static void GetCurrentAsyncId2(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + args.GetReturnValue().Set(env->async_hooks()->get_current_async_id_array()); +} + +static void GetNextAsyncId2(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + args.GetReturnValue().Set(env->async_hooks()->get_next_async_id_array()); +} + static bool FireAsyncInitCallbacksInternal( Environment* env, int64_t uid, @@ -243,7 +253,7 @@ void AsyncWrap::FireAsyncPreCallbacks( v8::Local uid, v8::Local obj) { - env->set_current_async_wrap_uid(uid->IntegerValue()); + env->async_hooks()->set_current_async_wrap_uid(uid->IntegerValue()); if (ranInitCallbacks) { Local pre_fn = env->async_hooks_pre_function(); @@ -283,7 +293,7 @@ void AsyncWrap::FireAsyncPostCallbacks(Environment* env, bool ranInitCallback, v } } - env->set_current_async_wrap_uid(0); + env->async_hooks()->set_current_async_wrap_uid(0); } static void NotifyAsyncEndFromJS(const FunctionCallbackInfo& args) { @@ -351,10 +361,9 @@ static void SetupHooks(const FunctionCallbackInfo& args) { env->set_async_hooks_destroy_function(destroy_v.As()); } - static void Initialize(Local target, - Local unused, - Local context) { + Local unused, + Local context) { Environment* env = Environment::GetCurrent(context); Isolate* isolate = env->isolate(); HandleScope scope(isolate); @@ -364,6 +373,8 @@ static void Initialize(Local target, env->SetMethod(target, "enable", EnableHooksJS); env->SetMethod(target, "getCurrentAsyncId", GetCurrentAsyncId); env->SetMethod(target, "getNextAsyncId", GetNextAsyncId); + env->SetMethod(target, "getCurrentAsyncId2", GetCurrentAsyncId2); + env->SetMethod(target, "getNextAsyncId2", GetNextAsyncId2); env->SetMethod(target, "notifyAsyncEnqueue", NotifyAsyncEnqueueFromJS); env->SetMethod(target, "notifyAsyncStart", NotifyAsyncStartFromJS); env->SetMethod(target, "notifyAsyncEnd", NotifyAsyncEndFromJS); diff --git a/src/env-inl.h b/src/env-inl.h index bf21d881579c18..8210a71901f40b 100644 --- a/src/env-inl.h +++ b/src/env-inl.h @@ -81,8 +81,34 @@ inline v8::Isolate* Environment::IsolateData::isolate() const { return isolate_; } -inline Environment::AsyncHooks::AsyncHooks() { - for (int i = 0; i < kFieldsCount; i++) fields_[i] = 0; +inline Environment::AsyncHooks::AsyncHooks(Environment* env) + : async_wrap_current_uid_(0), + async_wrap_counter_uid_(0), + env_(env) { + + for (int i = 0; i < kFieldsCount; i++) { + fields_[i] = 0; + } + + v8::HandleScope handle_scope(env_->isolate()); + + // set up int array for returing async_wrap_counter_uid_ value + const size_t array_length = sizeof(async_wrap_counter_uid_) / sizeof(int32_t); + static_assert(array_length == 2, "async_wrap_counter_uid_ unexpected size"); + v8::Local ab = + v8::ArrayBuffer::New(this->env_->isolate(), &async_wrap_counter_uid_, array_length); + v8::Local ua = + v8::Uint32Array::New(ab, 0, array_length); + this->async_wrap_next_id_array_.Reset(this->env_->isolate(), ua); + + // set up int array for returing async_wrap_current_uid_ value + const size_t array_length2 = sizeof(async_wrap_current_uid_) / sizeof(int32_t); + v8::Local ab2 = + v8::ArrayBuffer::New(this->env_->isolate(), &async_wrap_current_uid_, array_length); + v8::Local ua2 = + v8::Uint32Array::New(ab2, 0, array_length); + this->async_wrap_current_id_array_.Reset(this->env_->isolate(), ua2); + } inline uint32_t* Environment::AsyncHooks::fields() { @@ -216,13 +242,12 @@ inline Environment::Environment(v8::Local context, uv_loop_t* loop) : isolate_(context->GetIsolate()), isolate_data_(IsolateData::GetOrCreate(context->GetIsolate(), loop)), + async_hooks_(this), timer_base_(uv_now(loop)), using_domains_(false), printed_error_(false), trace_sync_io_(false), makecallback_cntr_(0), - async_wrap_counter_uid_(0), - async_wrap_current_uid_(0), debugger_agent_(this), http_parser_buffer_(nullptr), context_(context->GetIsolate(), context) { @@ -373,15 +398,24 @@ inline void Environment::set_trace_sync_io(bool value) { trace_sync_io_ = value; } -inline int64_t Environment::get_next_async_wrap_uid() { +inline int64_t Environment::AsyncHooks::get_next_async_wrap_uid() { return ++async_wrap_counter_uid_; } -inline int64_t Environment::get_current_async_wrap_uid() { +inline int64_t Environment::AsyncHooks::get_current_async_wrap_uid() { return this->async_wrap_current_uid_; } -inline void Environment::set_current_async_wrap_uid(int64_t value) { +inline v8::Local Environment::AsyncHooks::get_current_async_id_array() { + return async_wrap_current_id_array_.Get(this->env_->isolate()); +} + +inline v8::Local Environment::AsyncHooks::get_next_async_id_array() { + return async_wrap_next_id_array_.Get(this->env_->isolate()); +} + + +inline void Environment::AsyncHooks::set_current_async_wrap_uid(int64_t value) { this->async_wrap_current_uid_ = value; } diff --git a/src/env.h b/src/env.h index 12efca9f7c7237..6ad9cb21f87352 100644 --- a/src/env.h +++ b/src/env.h @@ -300,10 +300,20 @@ class Environment { inline int fields_count() const; inline bool callbacks_enabled(); inline void set_enable_callbacks(uint32_t flag); + inline int64_t get_next_async_wrap_uid(); + inline int64_t get_current_async_wrap_uid(); + inline void set_current_async_wrap_uid(int64_t value); + inline v8::Local get_current_async_id_array(); + inline v8::Local get_next_async_id_array(); private: friend class Environment; // So we can call the constructor. - inline AsyncHooks(); + inline AsyncHooks(Environment* env); + + int64_t async_wrap_counter_uid_; + int64_t async_wrap_current_uid_; + v8::Persistent async_wrap_current_id_array_; + v8::Persistent async_wrap_next_id_array_; enum Fields { // Set this to not zero if the init hook should be called. @@ -312,6 +322,7 @@ class Environment { }; uint32_t fields_[kFieldsCount]; + Environment* env_; DISALLOW_COPY_AND_ASSIGN(AsyncHooks); }; @@ -473,10 +484,6 @@ class Environment { void PrintSyncTrace() const; inline void set_trace_sync_io(bool value); - inline int64_t get_next_async_wrap_uid(); - inline int64_t get_current_async_wrap_uid(); - inline void set_current_async_wrap_uid(int64_t value); - inline uint32_t* heap_statistics_buffer() const; inline void set_heap_statistics_buffer(uint32_t* pointer); @@ -579,8 +586,7 @@ class Environment { bool printed_error_; bool trace_sync_io_; size_t makecallback_cntr_; - int64_t async_wrap_counter_uid_; - int64_t async_wrap_current_uid_; + debugger::Agent debugger_agent_; HandleWrapQueue handle_wrap_queue_;