@@ -69,109 +69,130 @@ using v8::Uint32;
6969using v8::Uint32Array;
7070using v8::Uint8Array;
7171using v8::Value;
72- using v8::WeakCallbackInfo;
7372
7473namespace {
7574
7675class CallbackInfo {
7776 public:
78- ~CallbackInfo ();
79-
80- static inline void Free (char * data, void * hint);
81- static inline CallbackInfo* New (Environment* env,
82- Local<ArrayBuffer> object,
83- FreeCallback callback,
84- char * data,
85- void * hint = nullptr );
77+ static inline Local<ArrayBuffer> CreateTrackedArrayBuffer (
78+ Environment* env,
79+ char * data,
80+ size_t length,
81+ FreeCallback callback,
82+ void * hint);
8683
8784 CallbackInfo (const CallbackInfo&) = delete ;
8885 CallbackInfo& operator =(const CallbackInfo&) = delete ;
8986
9087 private:
9188 static void CleanupHook (void * data);
92- static void WeakCallback ( const WeakCallbackInfo<CallbackInfo>& );
93- inline void WeakCallback (Isolate* isolate );
89+ inline void OnBackingStoreFree ( );
90+ inline void CallAndResetCallback ( );
9491 inline CallbackInfo (Environment* env,
95- Local<ArrayBuffer> object,
9692 FreeCallback callback,
9793 char * data,
9894 void * hint);
9995 Global<ArrayBuffer> persistent_;
100- FreeCallback const callback_;
96+ Mutex mutex_; // Protects callback_.
97+ FreeCallback callback_;
10198 char * const data_;
10299 void * const hint_;
103100 Environment* const env_;
104101};
105102
106103
107- void CallbackInfo::Free (char * data, void *) {
108- ::free (data);
109- }
110-
104+ Local<ArrayBuffer> CallbackInfo::CreateTrackedArrayBuffer (
105+ Environment* env,
106+ char * data,
107+ size_t length,
108+ FreeCallback callback,
109+ void * hint) {
110+ CHECK_NOT_NULL (callback);
111+ CHECK_IMPLIES (data == nullptr , length == 0 );
112+
113+ CallbackInfo* self = new CallbackInfo (env, callback, data, hint);
114+ std::unique_ptr<BackingStore> bs =
115+ ArrayBuffer::NewBackingStore (data, length, [](void *, size_t , void * arg) {
116+ static_cast <CallbackInfo*>(arg)->OnBackingStoreFree ();
117+ }, self);
118+ Local<ArrayBuffer> ab = ArrayBuffer::New (env->isolate (), std::move (bs));
119+
120+ // V8 simply ignores the BackingStore deleter callback if data == nullptr,
121+ // but our API contract requires it being called.
122+ if (data == nullptr ) {
123+ ab->Detach ();
124+ self->OnBackingStoreFree (); // This calls `callback` asynchronously.
125+ } else {
126+ // Store the ArrayBuffer so that we can detach it later.
127+ self->persistent_ .Reset (env->isolate (), ab);
128+ self->persistent_ .SetWeak ();
129+ }
111130
112- CallbackInfo* CallbackInfo::New (Environment* env,
113- Local<ArrayBuffer> object,
114- FreeCallback callback,
115- char * data,
116- void * hint) {
117- return new CallbackInfo (env, object, callback, data, hint);
131+ return ab;
118132}
119133
120134
121135CallbackInfo::CallbackInfo (Environment* env,
122- Local<ArrayBuffer> object,
123136 FreeCallback callback,
124137 char * data,
125138 void * hint)
126- : persistent_(env->isolate (), object),
127- callback_(callback),
139+ : callback_(callback),
128140 data_ (data),
129141 hint_(hint),
130142 env_(env) {
131- std::shared_ptr<BackingStore> obj_backing = object->GetBackingStore ();
132- CHECK_EQ (data_, static_cast <char *>(obj_backing->Data ()));
133- if (object->ByteLength () != 0 )
134- CHECK_NOT_NULL (data_);
135-
136- persistent_.SetWeak (this , WeakCallback, v8::WeakCallbackType::kParameter );
137143 env->AddCleanupHook (CleanupHook, this );
138144 env->isolate ()->AdjustAmountOfExternalAllocatedMemory (sizeof (*this ));
139145}
140146
141-
142- CallbackInfo::~CallbackInfo () {
143- persistent_.Reset ();
144- env_->RemoveCleanupHook (CleanupHook, this );
145- }
146-
147-
148147void CallbackInfo::CleanupHook (void * data) {
149148 CallbackInfo* self = static_cast <CallbackInfo*>(data);
150149
151150 {
152151 HandleScope handle_scope (self->env_ ->isolate ());
153152 Local<ArrayBuffer> ab = self->persistent_ .Get (self->env_ ->isolate ());
154- CHECK (!ab.IsEmpty ());
155- if (ab->IsDetachable ())
153+ if (!ab.IsEmpty () && ab->IsDetachable ()) {
156154 ab->Detach ();
155+ self->persistent_ .Reset ();
156+ }
157157 }
158158
159- self->WeakCallback (self->env_ ->isolate ());
159+ // Call the callback in this case, but don't delete `this` yet because the
160+ // BackingStore deleter callback will do so later.
161+ self->CallAndResetCallback ();
160162}
161163
164+ void CallbackInfo::CallAndResetCallback () {
165+ FreeCallback callback;
166+ {
167+ Mutex::ScopedLock lock (mutex_);
168+ callback = callback_;
169+ callback_ = nullptr ;
170+ }
171+ if (callback != nullptr ) {
172+ // Clean up all Environment-related state and run the callback.
173+ env_->RemoveCleanupHook (CleanupHook, this );
174+ int64_t change_in_bytes = -static_cast <int64_t >(sizeof (*this ));
175+ env_->isolate ()->AdjustAmountOfExternalAllocatedMemory (change_in_bytes);
162176
163- void CallbackInfo::WeakCallback (
164- const WeakCallbackInfo<CallbackInfo>& data) {
165- CallbackInfo* self = data.GetParameter ();
166- self->WeakCallback (data.GetIsolate ());
177+ callback (data_, hint_);
178+ }
167179}
168180
169-
170- void CallbackInfo::WeakCallback (Isolate* isolate) {
171- callback_ (data_, hint_);
172- int64_t change_in_bytes = -static_cast <int64_t >(sizeof (*this ));
173- isolate->AdjustAmountOfExternalAllocatedMemory (change_in_bytes);
174- delete this ;
181+ void CallbackInfo::OnBackingStoreFree () {
182+ // This method should always release the memory for `this`.
183+ std::unique_ptr<CallbackInfo> self { this };
184+ Mutex::ScopedLock lock (mutex_);
185+ // If callback_ == nullptr, that means that the callback has already run from
186+ // the cleanup hook, and there is nothing left to do here besides to clean
187+ // up the memory involved. In particular, the underlying `Environment` may
188+ // be gone at this point, so don’t attempt to call SetImmediateThreadsafe().
189+ if (callback_ == nullptr ) return ;
190+
191+ env_->SetImmediateThreadsafe ([self = std::move (self)](Environment* env) {
192+ CHECK_EQ (self->env_ , env); // Consistency check.
193+
194+ self->CallAndResetCallback ();
195+ });
175196}
176197
177198
@@ -408,26 +429,15 @@ MaybeLocal<Object> New(Environment* env,
408429 return Local<Object>();
409430 }
410431
411-
412- // The buffer will be released by a CallbackInfo::New() below,
413- // hence this BackingStore callback is empty.
414- std::unique_ptr<BackingStore> backing =
415- ArrayBuffer::NewBackingStore (data,
416- length,
417- [](void *, size_t , void *){},
418- nullptr );
419- Local<ArrayBuffer> ab = ArrayBuffer::New (env->isolate (),
420- std::move (backing));
432+ Local<ArrayBuffer> ab =
433+ CallbackInfo::CreateTrackedArrayBuffer (env, data, length, callback, hint);
421434 if (ab->SetPrivate (env->context (),
422435 env->arraybuffer_untransferable_private_symbol (),
423436 True (env->isolate ())).IsNothing ()) {
424- callback (data, hint);
425437 return Local<Object>();
426438 }
427439 MaybeLocal<Uint8Array> ui = Buffer::New (env, ab, 0 , length);
428440
429- CallbackInfo::New (env, ab, callback, data, hint);
430-
431441 if (ui.IsEmpty ())
432442 return MaybeLocal<Object>();
433443
0 commit comments