@@ -104,8 +104,17 @@ inline AliasedFloat64Array& AsyncHooks::async_ids_stack() {
104104 return async_ids_stack_;
105105}
106106
107- inline v8::Local<v8::Array> AsyncHooks::execution_async_resources () {
108- return PersistentToLocal::Strong (execution_async_resources_);
107+ v8::Local<v8::Array> AsyncHooks::js_execution_async_resources () {
108+ if (UNLIKELY (js_execution_async_resources_.IsEmpty ())) {
109+ js_execution_async_resources_.Reset (
110+ env ()->isolate (), v8::Array::New (env ()->isolate ()));
111+ }
112+ return PersistentToLocal::Strong (js_execution_async_resources_);
113+ }
114+
115+ v8::Local<v8::Object> AsyncHooks::native_execution_async_resource (size_t i) {
116+ if (i >= native_execution_async_resources_.size ()) return {};
117+ return PersistentToLocal::Strong (native_execution_async_resources_[i]);
109118}
110119
111120inline v8::Local<v8::String> AsyncHooks::provider_string (int idx) {
@@ -123,9 +132,7 @@ inline Environment* AsyncHooks::env() {
123132// Remember to keep this code aligned with pushAsyncContext() in JS.
124133inline void AsyncHooks::push_async_context (double async_id,
125134 double trigger_async_id,
126- v8::Local<v8::Value> resource) {
127- v8::HandleScope handle_scope (env ()->isolate ());
128-
135+ v8::Local<v8::Object> resource) {
129136 // Since async_hooks is experimental, do only perform the check
130137 // when async_hooks is enabled.
131138 if (fields_[kCheck ] > 0 ) {
@@ -142,8 +149,19 @@ inline void AsyncHooks::push_async_context(double async_id,
142149 async_id_fields_[kExecutionAsyncId ] = async_id;
143150 async_id_fields_[kTriggerAsyncId ] = trigger_async_id;
144151
145- auto resources = execution_async_resources ();
146- USE (resources->Set (env ()->context (), offset, resource));
152+ #ifdef DEBUG
153+ for (uint32_t i = offset; i < native_execution_async_resources_.size (); i++)
154+ CHECK (native_execution_async_resources_[i].IsEmpty ());
155+ #endif
156+
157+ // When this call comes from JS (as a way of increasing the stack size),
158+ // `resource` will be empty, because JS caches these values anyway, and
159+ // we should avoid creating strong global references that might keep
160+ // these JS resource objects alive longer than necessary.
161+ if (!resource.IsEmpty ()) {
162+ native_execution_async_resources_.resize (offset + 1 );
163+ native_execution_async_resources_[offset].Reset (env ()->isolate (), resource);
164+ }
147165}
148166
149167// Remember to keep this code aligned with popAsyncContext() in JS.
@@ -176,17 +194,45 @@ inline bool AsyncHooks::pop_async_context(double async_id) {
176194 async_id_fields_[kTriggerAsyncId ] = async_ids_stack_[2 * offset + 1 ];
177195 fields_[kStackLength ] = offset;
178196
179- auto resources = execution_async_resources ();
180- USE (resources->Delete (env ()->context (), offset));
197+ if (LIKELY (offset < native_execution_async_resources_.size () &&
198+ !native_execution_async_resources_[offset].IsEmpty ())) {
199+ #ifdef DEBUG
200+ for (uint32_t i = offset + 1 ;
201+ i < native_execution_async_resources_.size ();
202+ i++) {
203+ CHECK (native_execution_async_resources_[i].IsEmpty ());
204+ }
205+ #endif
206+ native_execution_async_resources_.resize (offset);
207+ if (native_execution_async_resources_.size () <
208+ native_execution_async_resources_.capacity () / 2 &&
209+ native_execution_async_resources_.size () > 16 ) {
210+ native_execution_async_resources_.shrink_to_fit ();
211+ }
212+ }
213+
214+ if (UNLIKELY (js_execution_async_resources ()->Length () > offset)) {
215+ v8::HandleScope handle_scope (env ()->isolate ());
216+ USE (js_execution_async_resources ()->Set (
217+ env ()->context (),
218+ env ()->length_string (),
219+ v8::Integer::NewFromUnsigned (env ()->isolate (), offset)));
220+ }
181221
182222 return fields_[kStackLength ] > 0 ;
183223}
184224
185- // Keep in sync with clearAsyncIdStack in lib/internal/async_hooks.js.
186- inline void AsyncHooks::clear_async_id_stack () {
187- auto isolate = env ()->isolate ();
225+ void AsyncHooks::clear_async_id_stack () {
226+ v8::Isolate* isolate = env ()->isolate ();
188227 v8::HandleScope handle_scope (isolate);
189- execution_async_resources_.Reset (isolate, v8::Array::New (isolate));
228+ if (!js_execution_async_resources_.IsEmpty ()) {
229+ USE (PersistentToLocal::Strong (js_execution_async_resources_)->Set (
230+ env ()->context (),
231+ env ()->length_string (),
232+ v8::Integer::NewFromUnsigned (isolate, 0 )));
233+ }
234+ native_execution_async_resources_.clear ();
235+ native_execution_async_resources_.shrink_to_fit ();
190236
191237 async_id_fields_[kExecutionAsyncId ] = 0 ;
192238 async_id_fields_[kTriggerAsyncId ] = 0 ;
0 commit comments