/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) |