diff options
Diffstat (limited to 'src/node_task_queue.cc')
-rw-r--r-- | src/node_task_queue.cc | 136 |
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) |