• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright Joyent, Inc. and other Node contributors.
2 //
3 // Permission is hereby granted, free of charge, to any person obtaining a
4 // copy of this software and associated documentation files (the
5 // "Software"), to deal in the Software without restriction, including
6 // without limitation the rights to use, copy, modify, merge, publish,
7 // distribute, sublicense, and/or sell copies of the Software, and to permit
8 // persons to whom the Software is furnished to do so, subject to the
9 // following conditions:
10 //
11 // The above copyright notice and this permission notice shall be included
12 // in all copies or substantial portions of the Software.
13 //
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15 // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
17 // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
18 // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19 // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
20 // USE OR OTHER DEALINGS IN THE SOFTWARE.
21 
22 #include "async_wrap.h"  // NOLINT(build/include_inline)
23 #include "async_wrap-inl.h"
24 #include "env-inl.h"
25 #include "node_errors.h"
26 #include "node_external_reference.h"
27 #include "tracing/traced_value.h"
28 #include "util-inl.h"
29 
30 #include "v8.h"
31 
32 using v8::Context;
33 using v8::DontDelete;
34 using v8::EscapableHandleScope;
35 using v8::Function;
36 using v8::FunctionCallbackInfo;
37 using v8::FunctionTemplate;
38 using v8::Global;
39 using v8::HandleScope;
40 using v8::Integer;
41 using v8::Isolate;
42 using v8::Local;
43 using v8::MaybeLocal;
44 using v8::Nothing;
45 using v8::Number;
46 using v8::Object;
47 using v8::PropertyAttribute;
48 using v8::ReadOnly;
49 using v8::String;
50 using v8::Undefined;
51 using v8::Value;
52 using v8::WeakCallbackInfo;
53 using v8::WeakCallbackType;
54 
55 using TryCatchScope = node::errors::TryCatchScope;
56 
57 namespace node {
58 
59 static const char* const provider_names[] = {
60 #define V(PROVIDER)                                                           \
61   #PROVIDER,
62   NODE_ASYNC_PROVIDER_TYPES(V)
63 #undef V
64 };
65 
DestroyAsyncIdsCallback(Environment * env)66 void AsyncWrap::DestroyAsyncIdsCallback(Environment* env) {
67   Local<Function> fn = env->async_hooks_destroy_function();
68 
69   TryCatchScope try_catch(env, TryCatchScope::CatchMode::kFatal);
70 
71   do {
72     std::vector<double> destroy_async_id_list;
73     destroy_async_id_list.swap(*env->destroy_async_id_list());
74     if (!env->can_call_into_js()) return;
75     for (auto async_id : destroy_async_id_list) {
76       // Want each callback to be cleaned up after itself, instead of cleaning
77       // them all up after the while() loop completes.
78       HandleScope scope(env->isolate());
79       Local<Value> async_id_value = Number::New(env->isolate(), async_id);
80       MaybeLocal<Value> ret = fn->Call(
81           env->context(), Undefined(env->isolate()), 1, &async_id_value);
82 
83       if (ret.IsEmpty())
84         return;
85     }
86   } while (!env->destroy_async_id_list()->empty());
87 }
88 
Emit(Environment * env,double async_id,AsyncHooks::Fields type,Local<Function> fn)89 void Emit(Environment* env, double async_id, AsyncHooks::Fields type,
90           Local<Function> fn) {
91   AsyncHooks* async_hooks = env->async_hooks();
92 
93   if (async_hooks->fields()[type] == 0 || !env->can_call_into_js())
94     return;
95 
96   HandleScope handle_scope(env->isolate());
97   Local<Value> async_id_value = Number::New(env->isolate(), async_id);
98   TryCatchScope try_catch(env, TryCatchScope::CatchMode::kFatal);
99   USE(fn->Call(env->context(), Undefined(env->isolate()), 1, &async_id_value));
100 }
101 
102 
EmitPromiseResolve(Environment * env,double async_id)103 void AsyncWrap::EmitPromiseResolve(Environment* env, double async_id) {
104   Emit(env, async_id, AsyncHooks::kPromiseResolve,
105        env->async_hooks_promise_resolve_function());
106 }
107 
108 
EmitTraceEventBefore()109 void AsyncWrap::EmitTraceEventBefore() {
110   switch (provider_type()) {
111 #define V(PROVIDER)                                                           \
112     case PROVIDER_ ## PROVIDER:                                               \
113       TRACE_EVENT_NESTABLE_ASYNC_BEGIN0(                                      \
114         TRACING_CATEGORY_NODE1(async_hooks),                                  \
115         #PROVIDER "_CALLBACK", static_cast<int64_t>(get_async_id()));         \
116       break;
117     NODE_ASYNC_PROVIDER_TYPES(V)
118 #undef V
119     default:
120       UNREACHABLE();
121   }
122 }
123 
124 
EmitBefore(Environment * env,double async_id)125 void AsyncWrap::EmitBefore(Environment* env, double async_id) {
126   Emit(env, async_id, AsyncHooks::kBefore,
127        env->async_hooks_before_function());
128 }
129 
130 
EmitTraceEventAfter(ProviderType type,double async_id)131 void AsyncWrap::EmitTraceEventAfter(ProviderType type, double async_id) {
132   switch (type) {
133 #define V(PROVIDER)                                                           \
134     case PROVIDER_ ## PROVIDER:                                               \
135       TRACE_EVENT_NESTABLE_ASYNC_END0(                                        \
136         TRACING_CATEGORY_NODE1(async_hooks),                                  \
137         #PROVIDER "_CALLBACK", static_cast<int64_t>(async_id));               \
138       break;
139     NODE_ASYNC_PROVIDER_TYPES(V)
140 #undef V
141     default:
142       UNREACHABLE();
143   }
144 }
145 
146 
EmitAfter(Environment * env,double async_id)147 void AsyncWrap::EmitAfter(Environment* env, double async_id) {
148   // If the user's callback failed then the after() hooks will be called at the
149   // end of _fatalException().
150   Emit(env, async_id, AsyncHooks::kAfter,
151        env->async_hooks_after_function());
152 }
153 
SetupHooks(const FunctionCallbackInfo<Value> & args)154 static void SetupHooks(const FunctionCallbackInfo<Value>& args) {
155   Environment* env = Environment::GetCurrent(args);
156 
157   CHECK(args[0]->IsObject());
158 
159   // All of init, before, after, destroy, and promise_resolve are supplied by
160   // async_hooks internally, so this should only ever be called once. At which
161   // time all the functions should be set. Detect this by checking if
162   // init !IsEmpty().
163   CHECK(env->async_hooks_init_function().IsEmpty());
164 
165   Local<Object> fn_obj = args[0].As<Object>();
166 
167 #define SET_HOOK_FN(name)                                                      \
168   do {                                                                         \
169     Local<Value> v =                                                           \
170         fn_obj->Get(env->context(),                                            \
171                     FIXED_ONE_BYTE_STRING(env->isolate(), #name))              \
172             .ToLocalChecked();                                                 \
173     CHECK(v->IsFunction());                                                    \
174     env->set_async_hooks_##name##_function(v.As<Function>());                  \
175   } while (0)
176 
177   SET_HOOK_FN(init);
178   SET_HOOK_FN(before);
179   SET_HOOK_FN(after);
180   SET_HOOK_FN(destroy);
181   SET_HOOK_FN(promise_resolve);
182 #undef SET_HOOK_FN
183 }
184 
SetPromiseHooks(const FunctionCallbackInfo<Value> & args)185 static void SetPromiseHooks(const FunctionCallbackInfo<Value>& args) {
186   Environment* env = Environment::GetCurrent(args);
187 
188   env->ResetPromiseHooks(
189       args[0]->IsFunction() ? args[0].As<Function>() : Local<Function>(),
190       args[1]->IsFunction() ? args[1].As<Function>() : Local<Function>(),
191       args[2]->IsFunction() ? args[2].As<Function>() : Local<Function>(),
192       args[3]->IsFunction() ? args[3].As<Function>() : Local<Function>());
193 }
194 
195 class DestroyParam {
196  public:
197   double asyncId;
198   Environment* env;
199   Global<Object> target;
200   Global<Object> propBag;
201 };
202 
DestroyParamCleanupHook(void * ptr)203 static void DestroyParamCleanupHook(void* ptr) {
204   delete static_cast<DestroyParam*>(ptr);
205 }
206 
WeakCallback(const WeakCallbackInfo<DestroyParam> & info)207 void AsyncWrap::WeakCallback(const WeakCallbackInfo<DestroyParam>& info) {
208   HandleScope scope(info.GetIsolate());
209 
210   std::unique_ptr<DestroyParam> p{info.GetParameter()};
211   Local<Object> prop_bag = PersistentToLocal::Default(info.GetIsolate(),
212                                                       p->propBag);
213   Local<Value> val;
214 
215   p->env->RemoveCleanupHook(DestroyParamCleanupHook, p.get());
216 
217   if (!prop_bag.IsEmpty() &&
218       !prop_bag->Get(p->env->context(), p->env->destroyed_string())
219         .ToLocal(&val)) {
220     return;
221   }
222 
223   if (val.IsEmpty() || val->IsFalse()) {
224     AsyncWrap::EmitDestroy(p->env, p->asyncId);
225   }
226   // unique_ptr goes out of scope here and pointer is deleted.
227 }
228 
229 
RegisterDestroyHook(const FunctionCallbackInfo<Value> & args)230 static void RegisterDestroyHook(const FunctionCallbackInfo<Value>& args) {
231   CHECK(args[0]->IsObject());
232   CHECK(args[1]->IsNumber());
233   CHECK(args.Length() == 2 || args[2]->IsObject());
234 
235   Isolate* isolate = args.GetIsolate();
236   DestroyParam* p = new DestroyParam();
237   p->asyncId = args[1].As<Number>()->Value();
238   p->env = Environment::GetCurrent(args);
239   p->target.Reset(isolate, args[0].As<Object>());
240   if (args.Length() > 2) {
241     p->propBag.Reset(isolate, args[2].As<Object>());
242   }
243   p->target.SetWeak(p, AsyncWrap::WeakCallback, WeakCallbackType::kParameter);
244   p->env->AddCleanupHook(DestroyParamCleanupHook, p);
245 }
246 
GetAsyncId(const FunctionCallbackInfo<Value> & args)247 void AsyncWrap::GetAsyncId(const FunctionCallbackInfo<Value>& args) {
248   AsyncWrap* wrap;
249   args.GetReturnValue().Set(kInvalidAsyncId);
250   ASSIGN_OR_RETURN_UNWRAP(&wrap, args.Holder());
251   args.GetReturnValue().Set(wrap->get_async_id());
252 }
253 
254 
PushAsyncContext(const FunctionCallbackInfo<Value> & args)255 void AsyncWrap::PushAsyncContext(const FunctionCallbackInfo<Value>& args) {
256   Environment* env = Environment::GetCurrent(args);
257   // No need for CHECK(IsNumber()) on args because if FromJust() doesn't fail
258   // then the checks in push_async_ids() and pop_async_id() will.
259   double async_id = args[0]->NumberValue(env->context()).FromJust();
260   double trigger_async_id = args[1]->NumberValue(env->context()).FromJust();
261   env->async_hooks()->push_async_context(async_id, trigger_async_id, {});
262 }
263 
264 
PopAsyncContext(const FunctionCallbackInfo<Value> & args)265 void AsyncWrap::PopAsyncContext(const FunctionCallbackInfo<Value>& args) {
266   Environment* env = Environment::GetCurrent(args);
267   double async_id = args[0]->NumberValue(env->context()).FromJust();
268   args.GetReturnValue().Set(env->async_hooks()->pop_async_context(async_id));
269 }
270 
271 
ExecutionAsyncResource(const FunctionCallbackInfo<Value> & args)272 void AsyncWrap::ExecutionAsyncResource(
273     const FunctionCallbackInfo<Value>& args) {
274   Environment* env = Environment::GetCurrent(args);
275   uint32_t index;
276   if (!args[0]->Uint32Value(env->context()).To(&index)) return;
277   args.GetReturnValue().Set(
278       env->async_hooks()->native_execution_async_resource(index));
279 }
280 
281 
ClearAsyncIdStack(const FunctionCallbackInfo<Value> & args)282 void AsyncWrap::ClearAsyncIdStack(const FunctionCallbackInfo<Value>& args) {
283   Environment* env = Environment::GetCurrent(args);
284   env->async_hooks()->clear_async_id_stack();
285 }
286 
287 
AsyncReset(const FunctionCallbackInfo<Value> & args)288 void AsyncWrap::AsyncReset(const FunctionCallbackInfo<Value>& args) {
289   CHECK(args[0]->IsObject());
290 
291   AsyncWrap* wrap;
292   ASSIGN_OR_RETURN_UNWRAP(&wrap, args.Holder());
293 
294   Local<Object> resource = args[0].As<Object>();
295   double execution_async_id =
296       args[1]->IsNumber() ? args[1].As<Number>()->Value() : kInvalidAsyncId;
297   wrap->AsyncReset(resource, execution_async_id);
298 }
299 
300 
GetProviderType(const FunctionCallbackInfo<Value> & args)301 void AsyncWrap::GetProviderType(const FunctionCallbackInfo<Value>& args) {
302   AsyncWrap* wrap;
303   args.GetReturnValue().Set(AsyncWrap::PROVIDER_NONE);
304   ASSIGN_OR_RETURN_UNWRAP(&wrap, args.Holder());
305   args.GetReturnValue().Set(wrap->provider_type());
306 }
307 
308 
EmitDestroy(bool from_gc)309 void AsyncWrap::EmitDestroy(bool from_gc) {
310   AsyncWrap::EmitDestroy(env(), async_id_);
311   // Ensure no double destroy is emitted via AsyncReset().
312   async_id_ = kInvalidAsyncId;
313 
314   if (!persistent().IsEmpty() && !from_gc) {
315     HandleScope handle_scope(env()->isolate());
316     USE(object()->Set(env()->context(), env()->resource_symbol(), object()));
317   }
318 }
319 
QueueDestroyAsyncId(const FunctionCallbackInfo<Value> & args)320 void AsyncWrap::QueueDestroyAsyncId(const FunctionCallbackInfo<Value>& args) {
321   CHECK(args[0]->IsNumber());
322   AsyncWrap::EmitDestroy(
323       Environment::GetCurrent(args),
324       args[0].As<Number>()->Value());
325 }
326 
SetCallbackTrampoline(const FunctionCallbackInfo<Value> & args)327 void AsyncWrap::SetCallbackTrampoline(const FunctionCallbackInfo<Value>& args) {
328   Environment* env = Environment::GetCurrent(args);
329 
330   if (args[0]->IsFunction()) {
331     env->set_async_hooks_callback_trampoline(args[0].As<Function>());
332   } else {
333     env->set_async_hooks_callback_trampoline(Local<Function>());
334   }
335 }
336 
GetConstructorTemplate(Environment * env)337 Local<FunctionTemplate> AsyncWrap::GetConstructorTemplate(Environment* env) {
338   Local<FunctionTemplate> tmpl = env->async_wrap_ctor_template();
339   if (tmpl.IsEmpty()) {
340     Isolate* isolate = env->isolate();
341     tmpl = NewFunctionTemplate(isolate, nullptr);
342     tmpl->SetClassName(FIXED_ONE_BYTE_STRING(env->isolate(), "AsyncWrap"));
343     tmpl->Inherit(BaseObject::GetConstructorTemplate(env));
344     SetProtoMethod(isolate, tmpl, "getAsyncId", AsyncWrap::GetAsyncId);
345     SetProtoMethod(isolate, tmpl, "asyncReset", AsyncWrap::AsyncReset);
346     SetProtoMethod(
347         isolate, tmpl, "getProviderType", AsyncWrap::GetProviderType);
348     env->set_async_wrap_ctor_template(tmpl);
349   }
350   return tmpl;
351 }
352 
Initialize(Local<Object> target,Local<Value> unused,Local<Context> context,void * priv)353 void AsyncWrap::Initialize(Local<Object> target,
354                            Local<Value> unused,
355                            Local<Context> context,
356                            void* priv) {
357   Environment* env = Environment::GetCurrent(context);
358   Isolate* isolate = env->isolate();
359   HandleScope scope(isolate);
360 
361   SetMethod(context, target, "setupHooks", SetupHooks);
362   SetMethod(context, target, "setCallbackTrampoline", SetCallbackTrampoline);
363   SetMethod(context, target, "pushAsyncContext", PushAsyncContext);
364   SetMethod(context, target, "popAsyncContext", PopAsyncContext);
365   SetMethod(context, target, "executionAsyncResource", ExecutionAsyncResource);
366   SetMethod(context, target, "clearAsyncIdStack", ClearAsyncIdStack);
367   SetMethod(context, target, "queueDestroyAsyncId", QueueDestroyAsyncId);
368   SetMethod(context, target, "setPromiseHooks", SetPromiseHooks);
369   SetMethod(context, target, "registerDestroyHook", RegisterDestroyHook);
370 
371   PropertyAttribute ReadOnlyDontDelete =
372       static_cast<PropertyAttribute>(ReadOnly | DontDelete);
373 
374 #define FORCE_SET_TARGET_FIELD(obj, str, field)                               \
375   (obj)->DefineOwnProperty(context,                                           \
376                            FIXED_ONE_BYTE_STRING(isolate, str),               \
377                            field,                                             \
378                            ReadOnlyDontDelete).FromJust()
379 
380   // Attach the uint32_t[] where each slot contains the count of the number of
381   // callbacks waiting to be called on a particular event. It can then be
382   // incremented/decremented from JS quickly to communicate to C++ if there are
383   // any callbacks waiting to be called.
384   FORCE_SET_TARGET_FIELD(target,
385                          "async_hook_fields",
386                          env->async_hooks()->fields().GetJSArray());
387 
388   // The following v8::Float64Array has 5 fields. These fields are shared in
389   // this way to allow JS and C++ to read/write each value as quickly as
390   // possible. The fields are represented as follows:
391   //
392   // kAsyncIdCounter: Maintains the state of the next unique id to be assigned.
393   //
394   // kDefaultTriggerAsyncId: Write the id of the resource responsible for a
395   //   handle's creation just before calling the new handle's constructor.
396   //   After the new handle is constructed kDefaultTriggerAsyncId is set back
397   //   to kInvalidAsyncId.
398   FORCE_SET_TARGET_FIELD(target,
399                          "async_id_fields",
400                          env->async_hooks()->async_id_fields().GetJSArray());
401 
402   FORCE_SET_TARGET_FIELD(target,
403                          "execution_async_resources",
404                          env->async_hooks()->js_execution_async_resources());
405 
406   target->Set(context,
407               env->async_ids_stack_string(),
408               env->async_hooks()->async_ids_stack().GetJSArray()).Check();
409 
410   Local<Object> constants = Object::New(isolate);
411 #define SET_HOOKS_CONSTANT(name)                                              \
412   FORCE_SET_TARGET_FIELD(                                                     \
413       constants, #name, Integer::New(isolate, AsyncHooks::name))
414 
415   SET_HOOKS_CONSTANT(kInit);
416   SET_HOOKS_CONSTANT(kBefore);
417   SET_HOOKS_CONSTANT(kAfter);
418   SET_HOOKS_CONSTANT(kDestroy);
419   SET_HOOKS_CONSTANT(kPromiseResolve);
420   SET_HOOKS_CONSTANT(kTotals);
421   SET_HOOKS_CONSTANT(kCheck);
422   SET_HOOKS_CONSTANT(kExecutionAsyncId);
423   SET_HOOKS_CONSTANT(kTriggerAsyncId);
424   SET_HOOKS_CONSTANT(kAsyncIdCounter);
425   SET_HOOKS_CONSTANT(kDefaultTriggerAsyncId);
426   SET_HOOKS_CONSTANT(kUsesExecutionAsyncResource);
427   SET_HOOKS_CONSTANT(kStackLength);
428 #undef SET_HOOKS_CONSTANT
429   FORCE_SET_TARGET_FIELD(target, "constants", constants);
430 
431   Local<Object> async_providers = Object::New(isolate);
432 #define V(p)                                                                  \
433   FORCE_SET_TARGET_FIELD(                                                     \
434       async_providers, #p, Integer::New(isolate, AsyncWrap::PROVIDER_ ## p));
435   NODE_ASYNC_PROVIDER_TYPES(V)
436 #undef V
437   FORCE_SET_TARGET_FIELD(target, "Providers", async_providers);
438 
439 #undef FORCE_SET_TARGET_FIELD
440 
441   env->set_async_hooks_init_function(Local<Function>());
442   env->set_async_hooks_before_function(Local<Function>());
443   env->set_async_hooks_after_function(Local<Function>());
444   env->set_async_hooks_destroy_function(Local<Function>());
445   env->set_async_hooks_promise_resolve_function(Local<Function>());
446   env->set_async_hooks_callback_trampoline(Local<Function>());
447   env->set_async_hooks_binding(target);
448 }
449 
RegisterExternalReferences(ExternalReferenceRegistry * registry)450 void AsyncWrap::RegisterExternalReferences(
451     ExternalReferenceRegistry* registry) {
452   registry->Register(SetupHooks);
453   registry->Register(SetCallbackTrampoline);
454   registry->Register(PushAsyncContext);
455   registry->Register(PopAsyncContext);
456   registry->Register(ExecutionAsyncResource);
457   registry->Register(ClearAsyncIdStack);
458   registry->Register(QueueDestroyAsyncId);
459   registry->Register(SetPromiseHooks);
460   registry->Register(RegisterDestroyHook);
461   registry->Register(AsyncWrap::GetAsyncId);
462   registry->Register(AsyncWrap::AsyncReset);
463   registry->Register(AsyncWrap::GetProviderType);
464 }
465 
AsyncWrap(Environment * env,Local<Object> object,ProviderType provider,double execution_async_id)466 AsyncWrap::AsyncWrap(Environment* env,
467                      Local<Object> object,
468                      ProviderType provider,
469                      double execution_async_id)
470     : AsyncWrap(env, object, provider, execution_async_id, false) {}
471 
AsyncWrap(Environment * env,Local<Object> object,ProviderType provider,double execution_async_id,bool silent)472 AsyncWrap::AsyncWrap(Environment* env,
473                      Local<Object> object,
474                      ProviderType provider,
475                      double execution_async_id,
476                      bool silent)
477     : AsyncWrap(env, object) {
478   CHECK_NE(provider, PROVIDER_NONE);
479   provider_type_ = provider;
480 
481   // Use AsyncReset() call to execute the init() callbacks.
482   AsyncReset(object, execution_async_id, silent);
483   init_hook_ran_ = true;
484 }
485 
AsyncWrap(Environment * env,Local<Object> object,ProviderType provider,double execution_async_id,double trigger_async_id)486 AsyncWrap::AsyncWrap(Environment* env,
487                      Local<Object> object,
488                      ProviderType provider,
489                      double execution_async_id,
490                      double trigger_async_id)
491     : AsyncWrap(env, object, provider, execution_async_id, true) {
492   trigger_async_id_ = trigger_async_id;
493 }
494 
AsyncWrap(Environment * env,Local<Object> object)495 AsyncWrap::AsyncWrap(Environment* env, Local<Object> object)
496   : BaseObject(env, object) {
497 }
498 
499 // This method is necessary to work around one specific problem:
500 // Before the init() hook runs, if there is one, the BaseObject() constructor
501 // registers this object with the Environment for finalization and debugging
502 // purposes.
503 // If the Environment decides to inspect this object for debugging, it tries to
504 // call virtual methods on this object that are only (meaningfully) implemented
505 // by the subclasses of AsyncWrap.
506 // This could, with bad luck, happen during the AsyncWrap() constructor,
507 // because we run JS code as part of it and that in turn can lead to a heapdump
508 // being taken, either through the inspector or our programmatic API for it.
509 // The object being initialized is not fully constructed at that point, and
510 // in particular its virtual function table points to the AsyncWrap one
511 // (as the subclass constructor has not yet begun execution at that point).
512 // This means that the functions that are used for heap dump memory tracking
513 // are not yet available, and trying to call them would crash the process.
514 // We use this particular `IsDoneInitializing()` method to tell the Environment
515 // that such debugging methods are not yet available.
516 // This may be somewhat unreliable when it comes to future changes, because
517 // at this point it *only* protects AsyncWrap subclasses, and *only* for cases
518 // where heap dumps are being taken while the init() hook is on the call stack.
519 // For now, it seems like the best solution, though.
IsDoneInitializing() const520 bool AsyncWrap::IsDoneInitializing() const {
521   return init_hook_ran_;
522 }
523 
~AsyncWrap()524 AsyncWrap::~AsyncWrap() {
525   EmitTraceEventDestroy();
526   EmitDestroy(true /* from gc */);
527 }
528 
EmitTraceEventDestroy()529 void AsyncWrap::EmitTraceEventDestroy() {
530   switch (provider_type()) {
531   #define V(PROVIDER)                                                         \
532     case PROVIDER_ ## PROVIDER:                                               \
533       TRACE_EVENT_NESTABLE_ASYNC_END0(                                        \
534         TRACING_CATEGORY_NODE1(async_hooks),                                  \
535         #PROVIDER, static_cast<int64_t>(get_async_id()));                     \
536       break;
537     NODE_ASYNC_PROVIDER_TYPES(V)
538   #undef V
539     default:
540       UNREACHABLE();
541   }
542 }
543 
EmitDestroy(Environment * env,double async_id)544 void AsyncWrap::EmitDestroy(Environment* env, double async_id) {
545   if (env->async_hooks()->fields()[AsyncHooks::kDestroy] == 0 ||
546       !env->can_call_into_js()) {
547     return;
548   }
549 
550   if (env->destroy_async_id_list()->empty()) {
551     env->SetImmediate(&DestroyAsyncIdsCallback, CallbackFlags::kUnrefed);
552   }
553 
554   // If the list gets very large empty it faster using a Microtask.
555   // Microtasks can't be added in GC context therefore we use an
556   // interrupt to get this Microtask scheduled as fast as possible.
557   if (env->destroy_async_id_list()->size() == 16384) {
558     env->RequestInterrupt([](Environment* env) {
559       env->context()->GetMicrotaskQueue()->EnqueueMicrotask(
560         env->isolate(),
561         [](void* arg) {
562           DestroyAsyncIdsCallback(static_cast<Environment*>(arg));
563         }, env);
564       });
565   }
566 
567   env->destroy_async_id_list()->push_back(async_id);
568 }
569 
570 // Generalized call for both the constructor and for handles that are pooled
571 // and reused over their lifetime. This way a new uid can be assigned when
572 // the resource is pulled out of the pool and put back into use.
AsyncReset(Local<Object> resource,double execution_async_id,bool silent)573 void AsyncWrap::AsyncReset(Local<Object> resource, double execution_async_id,
574                            bool silent) {
575   CHECK_NE(provider_type(), PROVIDER_NONE);
576 
577   if (async_id_ != kInvalidAsyncId) {
578     // This instance was in use before, we have already emitted an init with
579     // its previous async_id and need to emit a matching destroy for that
580     // before generating a new async_id.
581     EmitDestroy();
582   }
583 
584   // Now we can assign a new async_id_ to this instance.
585   async_id_ = execution_async_id == kInvalidAsyncId ? env()->new_async_id()
586                                                      : execution_async_id;
587   trigger_async_id_ = env()->get_default_trigger_async_id();
588 
589   {
590     HandleScope handle_scope(env()->isolate());
591     Local<Object> obj = object();
592     CHECK(!obj.IsEmpty());
593     if (resource != obj) {
594       USE(obj->Set(env()->context(), env()->resource_symbol(), resource));
595     }
596   }
597 
598   switch (provider_type()) {
599 #define V(PROVIDER)                                                           \
600     case PROVIDER_ ## PROVIDER:                                               \
601       if (*TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED(                        \
602           TRACING_CATEGORY_NODE1(async_hooks))) {                             \
603         auto data = tracing::TracedValue::Create();                           \
604         data->SetInteger("executionAsyncId",                                  \
605                          static_cast<int64_t>(env()->execution_async_id()));  \
606         data->SetInteger("triggerAsyncId",                                    \
607                          static_cast<int64_t>(get_trigger_async_id()));       \
608         TRACE_EVENT_NESTABLE_ASYNC_BEGIN1(                                    \
609           TRACING_CATEGORY_NODE1(async_hooks),                                \
610           #PROVIDER, static_cast<int64_t>(get_async_id()),                    \
611           "data", std::move(data));                                           \
612         }                                                                     \
613       break;
614     NODE_ASYNC_PROVIDER_TYPES(V)
615 #undef V
616     default:
617       UNREACHABLE();
618   }
619 
620   if (silent) return;
621 
622   EmitAsyncInit(env(), resource,
623                 env()->async_hooks()->provider_string(provider_type()),
624                 async_id_, trigger_async_id_);
625 }
626 
627 
EmitAsyncInit(Environment * env,Local<Object> object,Local<String> type,double async_id,double trigger_async_id)628 void AsyncWrap::EmitAsyncInit(Environment* env,
629                               Local<Object> object,
630                               Local<String> type,
631                               double async_id,
632                               double trigger_async_id) {
633   CHECK(!object.IsEmpty());
634   CHECK(!type.IsEmpty());
635   AsyncHooks* async_hooks = env->async_hooks();
636 
637   // Nothing to execute, so can continue normally.
638   if (async_hooks->fields()[AsyncHooks::kInit] == 0) {
639     return;
640   }
641 
642   HandleScope scope(env->isolate());
643   Local<Function> init_fn = env->async_hooks_init_function();
644 
645   Local<Value> argv[] = {
646     Number::New(env->isolate(), async_id),
647     type,
648     Number::New(env->isolate(), trigger_async_id),
649     object,
650   };
651 
652   TryCatchScope try_catch(env, TryCatchScope::CatchMode::kFatal);
653   USE(init_fn->Call(env->context(), object, arraysize(argv), argv));
654 }
655 
656 
MakeCallback(const Local<Function> cb,int argc,Local<Value> * argv)657 MaybeLocal<Value> AsyncWrap::MakeCallback(const Local<Function> cb,
658                                           int argc,
659                                           Local<Value>* argv) {
660   EmitTraceEventBefore();
661 
662   ProviderType provider = provider_type();
663   async_context context { get_async_id(), get_trigger_async_id() };
664   MaybeLocal<Value> ret = InternalMakeCallback(
665       env(), object(), object(), cb, argc, argv, context);
666 
667   // This is a static call with cached values because the `this` object may
668   // no longer be alive at this point.
669   EmitTraceEventAfter(provider, context.async_id);
670 
671   return ret;
672 }
673 
MemoryInfoName() const674 const char* AsyncWrap::MemoryInfoName() const {
675   return provider_names[provider_type()];
676 }
677 
diagnostic_name() const678 std::string AsyncWrap::diagnostic_name() const {
679   char buf[64];
680   snprintf(buf,
681            sizeof(buf),
682            "%s(%" PRIu64 ":%.0f)",
683            MemoryInfoName(),
684            env()->thread_id(),
685            async_id_);
686   return buf;
687 }
688 
GetOwner()689 Local<Object> AsyncWrap::GetOwner() {
690   return GetOwner(env(), object());
691 }
692 
GetOwner(Environment * env,Local<Object> obj)693 Local<Object> AsyncWrap::GetOwner(Environment* env, Local<Object> obj) {
694   EscapableHandleScope handle_scope(env->isolate());
695   CHECK(!obj.IsEmpty());
696 
697   TryCatchScope ignore_exceptions(env);
698   while (true) {
699     Local<Value> owner;
700     if (!obj->Get(env->context(),
701                   env->owner_symbol()).ToLocal(&owner) ||
702         !owner->IsObject()) {
703       return handle_scope.Escape(obj);
704     }
705 
706     obj = owner.As<Object>();
707   }
708 }
709 
710 }  // namespace node
711 
712 NODE_BINDING_CONTEXT_AWARE_INTERNAL(async_wrap, node::AsyncWrap::Initialize)
713 NODE_BINDING_EXTERNAL_REFERENCE(async_wrap,
714                                 node::AsyncWrap::RegisterExternalReferences)
715