Coverage Report

Created: 2025-09-05 10:05

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