[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