diff options
author | Joyee Cheung <joyeec9h3@gmail.com> | 2018-12-19 13:44:14 +0300 |
---|---|---|
committer | Joyee Cheung <joyeec9h3@gmail.com> | 2018-12-24 02:57:15 +0300 |
commit | 457603e96194b4858ad715f9faacb3ad7fec7f35 (patch) | |
tree | c9d496b06366721b7453b38c176b1cdbaa39fbe9 /src/node_task_queue.cc | |
parent | e830e2742cedceb7fc89de4910b2c1a9536a1638 (diff) |
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.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 00000000000..f65f420081d --- /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) |