summaryrefslogtreecommitdiff
path: root/src/node_task_queue.cc
diff options
context:
space:
mode:
authorJoyee Cheung <joyeec9h3@gmail.com>2018-12-19 18:44:14 +0800
committerJoyee Cheung <joyeec9h3@gmail.com>2018-12-24 07:57:15 +0800
commit457603e96194b4858ad715f9faacb3ad7fec7f35 (patch)
treec9d496b06366721b7453b38c176b1cdbaa39fbe9 /src/node_task_queue.cc
parente830e2742cedceb7fc89de4910b2c1a9536a1638 (diff)
downloadandroid-node-v8-457603e96194b4858ad715f9faacb3ad7fec7f35.tar.gz
android-node-v8-457603e96194b4858ad715f9faacb3ad7fec7f35.tar.bz2
android-node-v8-457603e96194b4858ad715f9faacb3ad7fec7f35.zip
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 <ruben@bridgewater.de> Reviewed-By: Anatoli Papirovski <apapirovski@mac.com>
Diffstat (limited to 'src/node_task_queue.cc')
-rw-r--r--src/node_task_queue.cc136
1 files changed, 136 insertions, 0 deletions
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 <atomic>
+
+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<Value>& args) {
+ args.GetIsolate()->RunMicrotasks();
+}
+
+static void SetTickCallback(const FunctionCallbackInfo<Value>& args) {
+ Environment* env = Environment::GetCurrent(args);
+ CHECK(args[0]->IsFunction());
+ env->set_tick_callback_function(args[0].As<Function>());
+}
+
+static void PromiseRejectCallback(PromiseRejectMessage message) {
+ static std::atomic<uint64_t> unhandledRejections{0};
+ static std::atomic<uint64_t> rejectionsHandledAfter{0};
+
+ Local<Promise> promise = message.GetPromise();
+ Isolate* isolate = promise->GetIsolate();
+ PromiseRejectEvent event = message.GetEvent();
+
+ Environment* env = Environment::GetCurrent(isolate);
+
+ if (env == nullptr) return;
+
+ Local<Function> callback = env->promise_reject_callback();
+ Local<Value> value;
+ Local<Value> 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<Value> args[] = { type, promise, value };
+ MaybeLocal<Value> 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<Value>& 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<Function>());
+}
+
+static void Initialize(Local<Object> target,
+ Local<Value> unused,
+ Local<Context> 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<Object> 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)