@@ -69,109 +69,130 @@ using v8::Uint32;
69
69
using v8::Uint32Array;
70
70
using v8::Uint8Array;
71
71
using v8::Value;
72
- using v8::WeakCallbackInfo;
73
72
74
73
namespace {
75
74
76
75
class CallbackInfo {
77
76
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);
86
83
87
84
CallbackInfo (const CallbackInfo&) = delete ;
88
85
CallbackInfo& operator =(const CallbackInfo&) = delete ;
89
86
90
87
private:
91
88
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 ( );
94
91
inline CallbackInfo (Environment* env,
95
- Local<ArrayBuffer> object,
96
92
FreeCallback callback,
97
93
char * data,
98
94
void * hint);
99
95
Global<ArrayBuffer> persistent_;
100
- FreeCallback const callback_;
96
+ Mutex mutex_; // Protects callback_.
97
+ FreeCallback callback_;
101
98
char * const data_;
102
99
void * const hint_;
103
100
Environment* const env_;
104
101
};
105
102
106
103
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
+ }
111
130
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;
118
132
}
119
133
120
134
121
135
CallbackInfo::CallbackInfo (Environment* env,
122
- Local<ArrayBuffer> object,
123
136
FreeCallback callback,
124
137
char * data,
125
138
void * hint)
126
- : persistent_(env->isolate (), object),
127
- callback_(callback),
139
+ : callback_(callback),
128
140
data_ (data),
129
141
hint_(hint),
130
142
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 );
137
143
env->AddCleanupHook (CleanupHook, this );
138
144
env->isolate ()->AdjustAmountOfExternalAllocatedMemory (sizeof (*this ));
139
145
}
140
146
141
-
142
- CallbackInfo::~CallbackInfo () {
143
- persistent_.Reset ();
144
- env_->RemoveCleanupHook (CleanupHook, this );
145
- }
146
-
147
-
148
147
void CallbackInfo::CleanupHook (void * data) {
149
148
CallbackInfo* self = static_cast <CallbackInfo*>(data);
150
149
151
150
{
152
151
HandleScope handle_scope (self->env_ ->isolate ());
153
152
Local<ArrayBuffer> ab = self->persistent_ .Get (self->env_ ->isolate ());
154
- CHECK (!ab.IsEmpty ());
155
- if (ab->IsDetachable ())
153
+ if (!ab.IsEmpty () && ab->IsDetachable ()) {
156
154
ab->Detach ();
155
+ self->persistent_ .Reset ();
156
+ }
157
157
}
158
158
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 ();
160
162
}
161
163
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);
162
176
163
- void CallbackInfo::WeakCallback (
164
- const WeakCallbackInfo<CallbackInfo>& data) {
165
- CallbackInfo* self = data.GetParameter ();
166
- self->WeakCallback (data.GetIsolate ());
177
+ callback (data_, hint_);
178
+ }
167
179
}
168
180
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
+ });
175
196
}
176
197
177
198
@@ -408,26 +429,15 @@ MaybeLocal<Object> New(Environment* env,
408
429
return Local<Object>();
409
430
}
410
431
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);
421
434
if (ab->SetPrivate (env->context (),
422
435
env->arraybuffer_untransferable_private_symbol (),
423
436
True (env->isolate ())).IsNothing ()) {
424
- callback (data, hint);
425
437
return Local<Object>();
426
438
}
427
439
MaybeLocal<Uint8Array> ui = Buffer::New (env, ab, 0 , length);
428
440
429
- CallbackInfo::New (env, ab, callback, data, hint);
430
-
431
441
if (ui.IsEmpty ())
432
442
return MaybeLocal<Object>();
433
443
0 commit comments