[Bf-blender-cvs] [542caff21ba] functions: custom reference counter that supports manual incref and decref
Jacques Lucke
noreply at git.blender.org
Sun Feb 10 20:26:06 CET 2019
Commit: 542caff21ba750a7985c1652b07676c4aa82d745
Author: Jacques Lucke
Date: Tue Feb 5 15:57:23 2019 +0100
Branches: functions
https://developer.blender.org/rB542caff21ba750a7985c1652b07676c4aa82d745
custom reference counter that supports manual incref and decref
===================================================================
A source/blender/blenlib/BLI_refcount.hpp
A tests/gtests/blenlib/BLI_refcount_test.cc
M tests/gtests/blenlib/CMakeLists.txt
===================================================================
diff --git a/source/blender/blenlib/BLI_refcount.hpp b/source/blender/blenlib/BLI_refcount.hpp
new file mode 100644
index 00000000000..83e0439f034
--- /dev/null
+++ b/source/blender/blenlib/BLI_refcount.hpp
@@ -0,0 +1,93 @@
+#include <atomic>
+#include <utility>
+
+namespace BLI {
+
+ template<typename T>
+ class RefCount {
+ private:
+ struct RefCountedObject {
+ RefCountedObject()
+ : m_value(nullptr), m_refcount(1) {}
+
+ T *m_value;
+ std::atomic<int> m_refcount;
+ };
+
+ RefCountedObject *m_object;
+ RefCount() = default;
+
+ public:
+ template<typename ...Args>
+ static RefCount<T> make(Args&&... args)
+ {
+ auto refcounted = RefCount<T>();
+ refcounted.m_object = new RefCountedObject();
+ refcounted.m_object->m_value = new T(std::forward<Args>(args)...);
+ return refcounted;
+ }
+
+ RefCount(const RefCount &other)
+ {
+ this->m_object = other.m_object;
+ this->incref();
+ }
+
+ RefCount(const RefCount &&other)
+ {
+ this->m_object = other.m_object;
+ this->incref();
+ }
+
+ ~RefCount()
+ {
+ this->decref();
+ }
+
+ RefCount &operator=(const RefCount &other)
+ {
+ if (this->m_object == other->m_object) {
+ return *this;
+ }
+
+ this->decref();
+ this->m_object = other.m_object;
+ this->incref();
+ return *this;
+ }
+
+ RefCount &operator=(const RefCount &&other)
+ {
+ this->decref();
+ this->m_object = other.m_object;
+ this->incref();
+ return *this;
+ }
+
+ void incref()
+ {
+ std::atomic_fetch_add(&this->m_object->m_refcount, 1);
+ }
+
+ void decref()
+ {
+ int previous_value = std::atomic_fetch_sub(&this->m_object->m_refcount, 1);
+ if (previous_value == 1) {
+ delete this->m_object->m_value;
+ delete this->m_object;
+ this->m_object = nullptr;
+ }
+ }
+
+ int refcount() const
+ {
+ return this->m_object->m_refcount;
+ }
+
+ T *operator->() const
+ {
+ return this->m_object->m_value;
+ }
+ };
+
+} /* namespace BLI */
\ No newline at end of file
diff --git a/tests/gtests/blenlib/BLI_refcount_test.cc b/tests/gtests/blenlib/BLI_refcount_test.cc
new file mode 100644
index 00000000000..54087d649cc
--- /dev/null
+++ b/tests/gtests/blenlib/BLI_refcount_test.cc
@@ -0,0 +1,105 @@
+#include "testing/testing.h"
+#include "BLI_refcount.hpp"
+
+#include <iostream>
+
+#define DEFAULT_VALUE 42
+
+class MyTestClass {
+public:
+ int m_value;
+ bool *m_alive = nullptr;
+
+ MyTestClass()
+ : m_value(DEFAULT_VALUE) {}
+
+ MyTestClass(int value)
+ : m_value(value) {}
+
+ MyTestClass(bool *alive)
+ : m_alive(alive)
+ {
+ *alive = true;
+ }
+
+ ~MyTestClass()
+ {
+ if (this->m_alive) *this->m_alive = false;
+ }
+};
+
+using namespace BLI;
+
+using TestObj = RefCount<MyTestClass>;
+
+TEST(refcount, OneReferenceAfterConstruction)
+{
+ TestObj obj = TestObj::make();
+ ASSERT_EQ(obj.refcount(), 1);
+}
+
+TEST(refcount, IncRefIncreasesRefCount)
+{
+ TestObj obj = TestObj::make();
+ ASSERT_EQ(obj.refcount(), 1);
+ obj.incref();
+ ASSERT_EQ(obj.refcount(), 2);
+}
+
+TEST(refcount, DecRefDecreasesRefCount)
+{
+ TestObj obj = TestObj::make();
+ obj.incref();
+ ASSERT_EQ(obj.refcount(), 2);
+ obj.decref();
+ ASSERT_EQ(obj.refcount(), 1);
+}
+
+TEST(refcount, CopyConstructorIncreasesRefCount)
+{
+ TestObj obj1 = TestObj::make();
+ ASSERT_EQ(obj1.refcount(), 1);
+ TestObj obj2(obj1);
+ ASSERT_EQ(obj1.refcount(), 2);
+ ASSERT_EQ(obj2.refcount(), 2);
+}
+
+TEST(refcount, MoveConstructorKeepsRefCount)
+{
+ TestObj obj(TestObj::make());
+ ASSERT_EQ(obj.refcount(), 1);
+}
+
+TEST(refcount, DecreasedWhenScopeEnds)
+{
+ TestObj obj1 = TestObj::make();
+ ASSERT_EQ(obj1.refcount(), 1);
+ {
+ TestObj obj2 = obj1;
+ ASSERT_EQ(obj1.refcount(), 2);
+ ASSERT_EQ(obj2.refcount(), 2);
+ }
+ ASSERT_EQ(obj1.refcount(), 1);
+}
+
+TEST(refcount, DefaultConstructorCalled)
+{
+ TestObj obj = TestObj::make();
+ ASSERT_EQ(obj->m_value, DEFAULT_VALUE);
+}
+
+TEST(refcount, OtherConstructorCalled)
+{
+ TestObj obj = TestObj::make(123);
+ ASSERT_EQ(obj->m_value, 123);
+}
+
+TEST(refcount, DestructorCalled)
+{
+ bool alive = false;
+ {
+ TestObj obj = TestObj::make(&alive);
+ ASSERT_TRUE(alive);
+ }
+ ASSERT_FALSE(alive);
+}
\ No newline at end of file
diff --git a/tests/gtests/blenlib/CMakeLists.txt b/tests/gtests/blenlib/CMakeLists.txt
index d7097399dac..466097fe364 100644
--- a/tests/gtests/blenlib/CMakeLists.txt
+++ b/tests/gtests/blenlib/CMakeLists.txt
@@ -55,6 +55,7 @@ BLENDER_TEST(BLI_math_geom "bf_blenlib")
BLENDER_TEST(BLI_memiter "bf_blenlib")
BLENDER_TEST(BLI_path_util "${BLI_path_util_extra_libs}")
BLENDER_TEST(BLI_polyfill_2d "bf_blenlib")
+BLENDER_TEST(BLI_refcount "bf_blenlib")
BLENDER_TEST(BLI_small_vector "bf_blenlib")
BLENDER_TEST(BLI_small_set_vector "bf_blenlib")
BLENDER_TEST(BLI_small_map "bf_blenlib")
More information about the Bf-blender-cvs
mailing list