@@ -162,6 +162,8 @@ class CowData {
162
162
return *out;
163
163
}
164
164
165
+ // Decrements the reference count. Deallocates the backing buffer if needed.
166
+ // After this function, _ptr is guaranteed to be NULL.
165
167
void _unref ();
166
168
void _ref (const CowData *p_from);
167
169
void _ref (const CowData &p_from);
@@ -252,7 +254,7 @@ class CowData {
252
254
Size count (const T &p_val) const ;
253
255
254
256
_FORCE_INLINE_ CowData () {}
255
- _FORCE_INLINE_ ~CowData ();
257
+ _FORCE_INLINE_ ~CowData () { _unref (); }
256
258
_FORCE_INLINE_ CowData (std::initializer_list<T> p_init);
257
259
_FORCE_INLINE_ CowData (const CowData<T> &p_from) { _ref (p_from); }
258
260
_FORCE_INLINE_ CowData (CowData<T> &&p_from) {
@@ -269,22 +271,30 @@ void CowData<T>::_unref() {
269
271
270
272
SafeNumeric<USize> *refc = _get_refcount ();
271
273
if (refc->decrement () > 0 ) {
272
- return ; // still in use
274
+ // Data is still in use elsewhere.
275
+ _ptr = nullptr ;
276
+ return ;
273
277
}
274
- // clean up
278
+ // Clean up.
279
+ // First, invalidate our own reference.
280
+ // NOTE: It is required to do so immediately because it must not be observable outside of this
281
+ // function after refcount has already been reduced to 0.
282
+ // WARNING: It must be done before calling the destructors, because one of them may otherwise
283
+ // observe it through a reference to us. In this case, it may try to access the buffer,
284
+ // which is illegal after some of the elements in it have already been destructed, and
285
+ // may lead to a segmentation fault.
286
+ USize current_size = *_get_size ();
287
+ T *prev_ptr = _ptr;
288
+ _ptr = nullptr ;
275
289
276
290
if constexpr (!std::is_trivially_destructible_v<T>) {
277
- USize current_size = *_get_size ();
278
-
279
291
for (USize i = 0 ; i < current_size; ++i) {
280
- // call destructors
281
- T *t = &_ptr[i];
282
- t->~T ();
292
+ prev_ptr[i].~T ();
283
293
}
284
294
}
285
295
286
296
// free mem
287
- Memory::free_static ((( uint8_t *)_ptr) - DATA_OFFSET, false );
297
+ Memory::free_static ((uint8_t *)prev_ptr - DATA_OFFSET, false );
288
298
}
289
299
290
300
template <typename T>
@@ -339,9 +349,8 @@ Error CowData<T>::resize(Size p_size) {
339
349
}
340
350
341
351
if (p_size == 0 ) {
342
- // wants to clean up
343
- _unref ();
344
- _ptr = nullptr ;
352
+ // Wants to clean up.
353
+ _unref (); // Resets _ptr to nullptr.
345
354
return OK;
346
355
}
347
356
@@ -484,8 +493,7 @@ void CowData<T>::_ref(const CowData &p_from) {
484
493
return ; // self assign, do nothing.
485
494
}
486
495
487
- _unref ();
488
- _ptr = nullptr ;
496
+ _unref (); // Resets _ptr to nullptr.
489
497
490
498
if (!p_from._ptr ) {
491
499
return ; // nothing to do
@@ -496,11 +504,6 @@ void CowData<T>::_ref(const CowData &p_from) {
496
504
}
497
505
}
498
506
499
- template <typename T>
500
- CowData<T>::~CowData () {
501
- _unref ();
502
- }
503
-
504
507
template <typename T>
505
508
CowData<T>::CowData(std::initializer_list<T> p_init) {
506
509
Error err = resize (p_init.size ());
0 commit comments