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