From 457603e96194b4858ad715f9faacb3ad7fec7f35 Mon Sep 17 00:00:00 2001 From: Joyee Cheung Date: Wed, 19 Dec 2018 18:44:14 +0800 Subject: src: move process.nextTick and promise setup into node_task_queue.cc This patch: - Moves the process.nextTick and promise setup C++ code into node_task_queue.cc which is exposed as `internalBinding('task_queue')` - Makes `lib/internal/process/promises.js` and `lib/internal/process/next_tick.js` as side-effect-free as possible - Removes the bootstrapper object being passed into `bootstrap/node.js`, let `next_tick.js` and `promises.js` load whatever they need from `internalBinding('task_queue')` instead. - Rename `process._tickCallback` to `runNextTicks` internally for clarity but still expose it as `process._tickCallback`. PR-URL: https://github.com/nodejs/node/pull/25163 Refs: https://github.com/nodejs/node/issues/24961 Reviewed-By: Ruben Bridgewater Reviewed-By: Anatoli Papirovski --- src/node_task_queue.cc | 136 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 136 insertions(+) create mode 100644 src/node_task_queue.cc (limited to 'src/node_task_queue.cc') diff --git a/src/node_task_queue.cc b/src/node_task_queue.cc new file mode 100644 index 0000000000..f65f420081 --- /dev/null +++ b/src/node_task_queue.cc @@ -0,0 +1,136 @@ +#include "env-inl.h" +#include "node.h" +#include "node_internals.h" +#include "v8.h" + +#include + +namespace node { + +using v8::Array; +using v8::Context; +using v8::Function; +using v8::FunctionCallbackInfo; +using v8::Isolate; +using v8::kPromiseHandlerAddedAfterReject; +using v8::kPromiseRejectAfterResolved; +using v8::kPromiseRejectWithNoHandler; +using v8::kPromiseResolveAfterResolved; +using v8::Local; +using v8::MaybeLocal; +using v8::Number; +using v8::Object; +using v8::Promise; +using v8::PromiseRejectEvent; +using v8::PromiseRejectMessage; +using v8::Value; + +namespace task_queue { + +static void RunMicrotasks(const FunctionCallbackInfo& args) { + args.GetIsolate()->RunMicrotasks(); +} + +static void SetTickCallback(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + CHECK(args[0]->IsFunction()); + env->set_tick_callback_function(args[0].As()); +} + +static void PromiseRejectCallback(PromiseRejectMessage message) { + static std::atomic unhandledRejections{0}; + static std::atomic rejectionsHandledAfter{0}; + + Local promise = message.GetPromise(); + Isolate* isolate = promise->GetIsolate(); + PromiseRejectEvent event = message.GetEvent(); + + Environment* env = Environment::GetCurrent(isolate); + + if (env == nullptr) return; + + Local callback = env->promise_reject_callback(); + Local value; + Local type = Number::New(env->isolate(), event); + + if (event == kPromiseRejectWithNoHandler) { + value = message.GetValue(); + unhandledRejections++; + TRACE_COUNTER2(TRACING_CATEGORY_NODE2(promises, rejections), + "rejections", + "unhandled", unhandledRejections, + "handledAfter", rejectionsHandledAfter); + } else if (event == kPromiseHandlerAddedAfterReject) { + value = Undefined(isolate); + rejectionsHandledAfter++; + TRACE_COUNTER2(TRACING_CATEGORY_NODE2(promises, rejections), + "rejections", + "unhandled", unhandledRejections, + "handledAfter", rejectionsHandledAfter); + } else if (event == kPromiseResolveAfterResolved) { + value = message.GetValue(); + } else if (event == kPromiseRejectAfterResolved) { + value = message.GetValue(); + } else { + return; + } + + if (value.IsEmpty()) { + value = Undefined(isolate); + } + + Local args[] = { type, promise, value }; + MaybeLocal ret = callback->Call(env->context(), + Undefined(isolate), + arraysize(args), + args); + + if (!ret.IsEmpty() && ret.ToLocalChecked()->IsTrue()) + env->tick_info()->promise_rejections_toggle_on(); +} + +static void InitializePromiseRejectCallback( + const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + Isolate* isolate = env->isolate(); + + CHECK(args[0]->IsFunction()); + + // TODO(joyeecheung): this may be moved to somewhere earlier in the bootstrap + // to make sure it's only called once + isolate->SetPromiseRejectCallback(PromiseRejectCallback); + + env->set_promise_reject_callback(args[0].As()); +} + +static void Initialize(Local target, + Local unused, + Local context, + void* priv) { + Environment* env = Environment::GetCurrent(context); + Isolate* isolate = env->isolate(); + + env->SetMethod(target, "setTickCallback", SetTickCallback); + env->SetMethod(target, "runMicrotasks", RunMicrotasks); + target->Set(env->context(), + FIXED_ONE_BYTE_STRING(isolate, "tickInfo"), + env->tick_info()->fields().GetJSArray()).FromJust(); + + Local events = Object::New(isolate); + NODE_DEFINE_CONSTANT(events, kPromiseRejectWithNoHandler); + NODE_DEFINE_CONSTANT(events, kPromiseHandlerAddedAfterReject); + NODE_DEFINE_CONSTANT(events, kPromiseResolveAfterResolved); + NODE_DEFINE_CONSTANT(events, kPromiseRejectAfterResolved); + + target->Set(env->context(), + FIXED_ONE_BYTE_STRING(isolate, "promiseRejectEvents"), + events).FromJust(); + env->SetMethod(target, + "initializePromiseRejectCallback", + InitializePromiseRejectCallback); +} + +} // namespace task_queue +} // namespace node + +NODE_MODULE_CONTEXT_AWARE_INTERNAL(task_queue, node::task_queue::Initialize) -- cgit v1.2.3