|
@@ -27,9 +27,9 @@ TypeHandle RefCountObj<Base>::_type_handle;
|
|
|
* inheritance.
|
|
* inheritance.
|
|
|
*/
|
|
*/
|
|
|
INLINE ReferenceCount::
|
|
INLINE ReferenceCount::
|
|
|
-ReferenceCount() {
|
|
|
|
|
- _weak_list = nullptr;
|
|
|
|
|
- _ref_count = 0;
|
|
|
|
|
|
|
+ReferenceCount() :
|
|
|
|
|
+ _weak_list(nullptr),
|
|
|
|
|
+ _ref_count(0) {
|
|
|
#ifdef DO_MEMORY_USAGE
|
|
#ifdef DO_MEMORY_USAGE
|
|
|
MemoryUsage::record_pointer(this);
|
|
MemoryUsage::record_pointer(this);
|
|
|
#endif
|
|
#endif
|
|
@@ -44,9 +44,9 @@ ReferenceCount() {
|
|
|
* try.
|
|
* try.
|
|
|
*/
|
|
*/
|
|
|
INLINE ReferenceCount::
|
|
INLINE ReferenceCount::
|
|
|
-ReferenceCount(const ReferenceCount &) {
|
|
|
|
|
- _weak_list = nullptr;
|
|
|
|
|
- _ref_count = 0;
|
|
|
|
|
|
|
+ReferenceCount(const ReferenceCount &) :
|
|
|
|
|
+ _weak_list(nullptr),
|
|
|
|
|
+ _ref_count(0) {
|
|
|
#ifdef DO_MEMORY_USAGE
|
|
#ifdef DO_MEMORY_USAGE
|
|
|
MemoryUsage::record_pointer(this);
|
|
MemoryUsage::record_pointer(this);
|
|
|
#endif
|
|
#endif
|
|
@@ -69,7 +69,7 @@ operator = (const ReferenceCount &) {
|
|
|
// to create an automatic (local variable) instance of a class that derives
|
|
// to create an automatic (local variable) instance of a class that derives
|
|
|
// from ReferenceCount. Or maybe your headers are out of sync, and you need
|
|
// from ReferenceCount. Or maybe your headers are out of sync, and you need
|
|
|
// to make clean in direct or some higher tree.
|
|
// to make clean in direct or some higher tree.
|
|
|
- nassertv(_ref_count != deleted_ref_count);
|
|
|
|
|
|
|
+ nassertv(_ref_count.load(std::memory_order_relaxed) != deleted_ref_count);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -78,23 +78,29 @@ operator = (const ReferenceCount &) {
|
|
|
ReferenceCount::
|
|
ReferenceCount::
|
|
|
~ReferenceCount() {
|
|
~ReferenceCount() {
|
|
|
TAU_PROFILE("ReferenceCount::~ReferenceCount()", " ", TAU_USER);
|
|
TAU_PROFILE("ReferenceCount::~ReferenceCount()", " ", TAU_USER);
|
|
|
|
|
+
|
|
|
|
|
+ // We can safely use relaxed ordering for everything in this destructor,
|
|
|
|
|
+ // since (1) we already issued an acquire barrier before invoking delete,
|
|
|
|
|
+ // and (2) we are the only thread accessing this object at this point.
|
|
|
|
|
+ int ref_count = _ref_count.load(std::memory_order_relaxed);
|
|
|
|
|
+
|
|
|
// If this assertion fails, we're trying to delete an object that was just
|
|
// If this assertion fails, we're trying to delete an object that was just
|
|
|
// deleted. Possibly you used a real pointer instead of a PointerTo at some
|
|
// deleted. Possibly you used a real pointer instead of a PointerTo at some
|
|
|
// point, and the object was deleted when the PointerTo went out of scope.
|
|
// point, and the object was deleted when the PointerTo went out of scope.
|
|
|
// Maybe you tried to create an automatic (local variable) instance of a
|
|
// Maybe you tried to create an automatic (local variable) instance of a
|
|
|
// class that derives from ReferenceCount. Or maybe your headers are out of
|
|
// class that derives from ReferenceCount. Or maybe your headers are out of
|
|
|
// sync, and you need to make clean in direct or some higher tree.
|
|
// sync, and you need to make clean in direct or some higher tree.
|
|
|
- nassertv(_ref_count != deleted_ref_count);
|
|
|
|
|
|
|
+ nassertv(ref_count != deleted_ref_count);
|
|
|
|
|
|
|
|
// If this assertion fails, we're trying to delete a static object that
|
|
// If this assertion fails, we're trying to delete a static object that
|
|
|
// still has an outstanding reference count. You should make sure that all
|
|
// still has an outstanding reference count. You should make sure that all
|
|
|
// references to your static objects are gone by the time the object itself
|
|
// references to your static objects are gone by the time the object itself
|
|
|
// destructs.
|
|
// destructs.
|
|
|
- nassertv(_ref_count <= local_ref_count);
|
|
|
|
|
|
|
+ nassertv(ref_count <= local_ref_count);
|
|
|
|
|
|
|
|
// If this assertion fails, the reference counts are all screwed up
|
|
// If this assertion fails, the reference counts are all screwed up
|
|
|
// altogether. Maybe some errant code stomped all over memory somewhere.
|
|
// altogether. Maybe some errant code stomped all over memory somewhere.
|
|
|
- nassertv(_ref_count >= 0);
|
|
|
|
|
|
|
+ nassertv(ref_count >= 0);
|
|
|
|
|
|
|
|
// If this assertion fails, someone tried to delete this object while its
|
|
// If this assertion fails, someone tried to delete this object while its
|
|
|
// reference count was still positive. Maybe you tried to point a PointerTo
|
|
// reference count was still positive. Maybe you tried to point a PointerTo
|
|
@@ -105,19 +111,20 @@ ReferenceCount::
|
|
|
// Another possibility is you inadvertently omitted a copy constructor for a
|
|
// Another possibility is you inadvertently omitted a copy constructor for a
|
|
|
// ReferenceCount object, and then bitwise copied a dynamically allocated
|
|
// ReferenceCount object, and then bitwise copied a dynamically allocated
|
|
|
// value--reference count and all--onto a locally allocated one.
|
|
// value--reference count and all--onto a locally allocated one.
|
|
|
- nassertv(_ref_count == 0 || _ref_count == local_ref_count);
|
|
|
|
|
|
|
+ nassertv(ref_count == 0 || ref_count == local_ref_count);
|
|
|
|
|
|
|
|
// Tell our weak reference holders that we're going away now.
|
|
// Tell our weak reference holders that we're going away now.
|
|
|
- if (_weak_list != nullptr) {
|
|
|
|
|
|
|
+ WeakReferenceList *weak_list = _weak_list.load(std::memory_order_relaxed);
|
|
|
|
|
+ if (weak_list != nullptr) {
|
|
|
((WeakReferenceList *)_weak_list)->mark_deleted();
|
|
((WeakReferenceList *)_weak_list)->mark_deleted();
|
|
|
- _weak_list = nullptr;
|
|
|
|
|
|
|
+ _weak_list.store(nullptr, std::memory_order_release);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
#ifndef NDEBUG
|
|
#ifndef NDEBUG
|
|
|
// Ok, all clear to delete. Now set the reference count to
|
|
// Ok, all clear to delete. Now set the reference count to
|
|
|
// deleted_ref_count, so we'll have a better chance of noticing if we happen
|
|
// deleted_ref_count, so we'll have a better chance of noticing if we happen
|
|
|
// to have a stray pointer to it still out there.
|
|
// to have a stray pointer to it still out there.
|
|
|
- _ref_count = deleted_ref_count;
|
|
|
|
|
|
|
+ _ref_count.store(deleted_ref_count, std::memory_order_relaxed);
|
|
|
#endif
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef DO_MEMORY_USAGE
|
|
#ifdef DO_MEMORY_USAGE
|
|
@@ -133,7 +140,7 @@ get_ref_count() const {
|
|
|
#ifdef _DEBUG
|
|
#ifdef _DEBUG
|
|
|
test_ref_count_integrity();
|
|
test_ref_count_integrity();
|
|
|
#endif
|
|
#endif
|
|
|
- return (int)AtomicAdjust::get(_ref_count);
|
|
|
|
|
|
|
+ return _ref_count.load(std::memory_order_acquire);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -154,7 +161,7 @@ ref() const {
|
|
|
nassertv(test_ref_count_integrity());
|
|
nassertv(test_ref_count_integrity());
|
|
|
#endif
|
|
#endif
|
|
|
|
|
|
|
|
- AtomicAdjust::inc(_ref_count);
|
|
|
|
|
|
|
+ _ref_count.fetch_add(1, std::memory_order_relaxed);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -184,9 +191,9 @@ unref() const {
|
|
|
// If this assertion fails, you tried to unref an object with a zero
|
|
// If this assertion fails, you tried to unref an object with a zero
|
|
|
// reference count. Are you using ref() and unref() directly? Are you sure
|
|
// reference count. Are you using ref() and unref() directly? Are you sure
|
|
|
// you can't use PointerTo's?
|
|
// you can't use PointerTo's?
|
|
|
- nassertr(_ref_count > 0, 0);
|
|
|
|
|
|
|
+ nassertr(_ref_count.load(std::memory_order_relaxed) > 0, 0);
|
|
|
#endif
|
|
#endif
|
|
|
- return AtomicAdjust::dec(_ref_count);
|
|
|
|
|
|
|
+ return _ref_count.fetch_sub(1, std::memory_order_release) != 1;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -227,11 +234,11 @@ test_ref_count_nonzero() const {
|
|
|
*/
|
|
*/
|
|
|
INLINE void ReferenceCount::
|
|
INLINE void ReferenceCount::
|
|
|
local_object() {
|
|
local_object() {
|
|
|
|
|
+ int prev_count = _ref_count.exchange(local_ref_count, std::memory_order_relaxed);
|
|
|
|
|
+
|
|
|
// If this assertion fails, you didn't call this immediately after creating
|
|
// If this assertion fails, you didn't call this immediately after creating
|
|
|
// a local object.
|
|
// a local object.
|
|
|
- nassertv(_ref_count == 0);
|
|
|
|
|
-
|
|
|
|
|
- _ref_count = local_ref_count;
|
|
|
|
|
|
|
+ nassertv(prev_count == 0);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -242,7 +249,7 @@ local_object() {
|
|
|
*/
|
|
*/
|
|
|
INLINE bool ReferenceCount::
|
|
INLINE bool ReferenceCount::
|
|
|
has_weak_list() const {
|
|
has_weak_list() const {
|
|
|
- return _weak_list != nullptr;
|
|
|
|
|
|
|
+ return _weak_list.load(std::memory_order_relaxed) != nullptr;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -255,10 +262,10 @@ has_weak_list() const {
|
|
|
*/
|
|
*/
|
|
|
INLINE WeakReferenceList *ReferenceCount::
|
|
INLINE WeakReferenceList *ReferenceCount::
|
|
|
get_weak_list() const {
|
|
get_weak_list() const {
|
|
|
- if (AtomicAdjust::get_ptr(_weak_list) == nullptr) {
|
|
|
|
|
|
|
+ if (_weak_list.load(std::memory_order_relaxed) == nullptr) {
|
|
|
((ReferenceCount *)this)->create_weak_list();
|
|
((ReferenceCount *)this)->create_weak_list();
|
|
|
}
|
|
}
|
|
|
- return (WeakReferenceList *)AtomicAdjust::get_ptr(_weak_list);
|
|
|
|
|
|
|
+ return _weak_list.load(std::memory_order_consume);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -273,7 +280,7 @@ weak_ref() {
|
|
|
#ifdef _DEBUG
|
|
#ifdef _DEBUG
|
|
|
nassertr(test_ref_count_integrity(), nullptr);
|
|
nassertr(test_ref_count_integrity(), nullptr);
|
|
|
#else
|
|
#else
|
|
|
- nassertr(_ref_count != deleted_ref_count, nullptr);
|
|
|
|
|
|
|
+ nassertr(_ref_count.load(std::memory_order_relaxed) != deleted_ref_count, nullptr);
|
|
|
#endif
|
|
#endif
|
|
|
WeakReferenceList *weak_ref = get_weak_list();
|
|
WeakReferenceList *weak_ref = get_weak_list();
|
|
|
weak_ref->ref();
|
|
weak_ref->ref();
|
|
@@ -290,7 +297,7 @@ weak_unref() {
|
|
|
#ifdef _DEBUG
|
|
#ifdef _DEBUG
|
|
|
nassertv(test_ref_count_integrity());
|
|
nassertv(test_ref_count_integrity());
|
|
|
#endif
|
|
#endif
|
|
|
- WeakReferenceList *weak_list = (WeakReferenceList *)_weak_list;
|
|
|
|
|
|
|
+ WeakReferenceList *weak_list = _weak_list.load(std::memory_order_consume);
|
|
|
nassertv(weak_list != nullptr);
|
|
nassertv(weak_list != nullptr);
|
|
|
bool nonzero = weak_list->unref();
|
|
bool nonzero = weak_list->unref();
|
|
|
nassertv(nonzero);
|
|
nassertv(nonzero);
|
|
@@ -307,13 +314,16 @@ ref_if_nonzero() const {
|
|
|
#ifdef _DEBUG
|
|
#ifdef _DEBUG
|
|
|
test_ref_count_integrity();
|
|
test_ref_count_integrity();
|
|
|
#endif
|
|
#endif
|
|
|
- AtomicAdjust::Integer ref_count;
|
|
|
|
|
|
|
+ int ref_count = _ref_count.load(std::memory_order_relaxed);
|
|
|
do {
|
|
do {
|
|
|
- ref_count = AtomicAdjust::get(_ref_count);
|
|
|
|
|
if (ref_count <= 0) {
|
|
if (ref_count <= 0) {
|
|
|
return false;
|
|
return false;
|
|
|
}
|
|
}
|
|
|
- } while (ref_count != AtomicAdjust::compare_and_exchange(_ref_count, ref_count, ref_count + 1));
|
|
|
|
|
|
|
+ }
|
|
|
|
|
+ while (!_ref_count.compare_exchange_weak(ref_count, ref_count + 1,
|
|
|
|
|
+ std::memory_order_seq_cst,
|
|
|
|
|
+ std::memory_order_relaxed));
|
|
|
|
|
+
|
|
|
return true;
|
|
return true;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -321,15 +331,21 @@ ref_if_nonzero() const {
|
|
|
* Atomically decreases the reference count of this object if it is one.
|
|
* Atomically decreases the reference count of this object if it is one.
|
|
|
* Do not use this. This exists only to implement a special case with the
|
|
* Do not use this. This exists only to implement a special case with the
|
|
|
* state cache.
|
|
* state cache.
|
|
|
- * @return false if the reference count was decremented to zero.
|
|
|
|
|
|
|
+ * @return false on success, ie. if the reference count was decremented to 0.
|
|
|
*/
|
|
*/
|
|
|
INLINE bool ReferenceCount::
|
|
INLINE bool ReferenceCount::
|
|
|
unref_if_one() const {
|
|
unref_if_one() const {
|
|
|
#ifdef _DEBUG
|
|
#ifdef _DEBUG
|
|
|
nassertr(test_ref_count_integrity(), 0);
|
|
nassertr(test_ref_count_integrity(), 0);
|
|
|
- nassertr(_ref_count > 0, 0);
|
|
|
|
|
|
|
+ nassertr(_ref_count.load(std::memory_order_relaxed) > 0, 0);
|
|
|
#endif
|
|
#endif
|
|
|
- return (AtomicAdjust::compare_and_exchange(_ref_count, 1, 0) != 1);
|
|
|
|
|
|
|
+
|
|
|
|
|
+ // Presumably if the ref count becomes 0, someone is about to delete the
|
|
|
|
|
+ // object or something like that, hence the acquire order on success.
|
|
|
|
|
+ int expected = 1;
|
|
|
|
|
+ return !_ref_count.compare_exchange_strong(expected, 0,
|
|
|
|
|
+ std::memory_order_acquire,
|
|
|
|
|
+ std::memory_order_relaxed);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -350,6 +366,7 @@ unref_delete(RefCountType *ptr) {
|
|
|
|
|
|
|
|
if (!ptr->unref()) {
|
|
if (!ptr->unref()) {
|
|
|
// If the reference count has gone to zero, delete the object.
|
|
// If the reference count has gone to zero, delete the object.
|
|
|
|
|
+ patomic_thread_fence(std::memory_order_acquire);
|
|
|
delete ptr;
|
|
delete ptr;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|