Selaa lähdekoodia

ObserverPtr optimized. No longer depending on shared/weak pointer.

Michael Ragazzon 6 vuotta sitten
vanhempi
sitoutus
601e126715
2 muutettua tiedostoa jossa 125 lisäystä ja 21 poistoa
  1. 106 21
      Include/RmlUi/Core/ObserverPtr.h
  2. 19 0
      Source/Core/ObserverPtr.cpp

+ 106 - 21
Include/RmlUi/Core/ObserverPtr.h

@@ -29,61 +29,146 @@
 #ifndef RMLUIOBSERVERPTR_H
 #define RMLUIOBSERVERPTR_H
 
+#include <utility>
+#include <type_traits>
 #include "Types.h"
 
 namespace Rml {
 namespace Core {
 
+struct RMLUICORE_API ObserverPtrBlock {
+	int num_observers = 0;
+	void* pointed_to_object;
+};
+RMLUICORE_API ObserverPtrBlock* AllocateObserverPtrBlock();
+RMLUICORE_API void DeallocateObserverPtrBlockIfEmpty(ObserverPtrBlock* block);
+
+template<typename T>
+class EnableObserverPtr;
+
+
 /**
 	Observer pointer.
 
-	Holds a weak reference to something owned by someone else. Will return false if the pointed to object was destroyed.
+	Holds a weak reference to an object owned by someone else. Can tell whether or not the pointed to object has been destroyed.
+
+	Usage: Given a class T, derive from EnableObserverPtr<T>. Then, we can use the observer pointer as follows:
+
+		auto object = std::make_unique<T>();
+		ObserverPtr<T> observer_ptr = object->GetObserverPtr();
+		// ...
+		if(obserer_ptr) { 
+			// Will only enter if object is still alive.
+			observer_ptr->do_a_thing(); 
+		} 
 
 	Note: Not thread safe.
  */
 
 template<typename T>
-class ObserverPtr {
+class RMLUICORE_API ObserverPtr {
 public:
-	ObserverPtr(std::weak_ptr<uintptr_t> ptr) : ptr(ptr) {}
+	ObserverPtr() noexcept : block(nullptr) {}
+	~ObserverPtr() noexcept {
+		reset(); 
+	}
 
-	explicit operator bool() const { return !ptr.expired(); }
+	// Copy
+	ObserverPtr<T>(const ObserverPtr<T>& other) noexcept : block(other.block) {
+		if (block)
+			block->num_observers += 1;
+	}
+	ObserverPtr<T>& operator=(const ObserverPtr<T>& other) noexcept {
+		reset();
+		block = other.block;
+		if (block)
+			block->num_observers += 1;
+		return *this;
+	}
 
-	T* get() {
-		// TODO: Locking it here here does nothing really, but required because we are basing this on a std::shared_ptr for now.
-		auto lock = ptr.lock();
-		if (!lock)
-			return nullptr;
-		return reinterpret_cast<T*>(*lock);
+	// Move
+	ObserverPtr<T>(ObserverPtr<T>&& other) noexcept : block(std::exchange(other.block, nullptr)) {}
+	ObserverPtr<T>& operator=(ObserverPtr<T>&& other) noexcept
+	{
+		reset();
+		block = std::exchange(other.block, nullptr);
+		return *this;
 	}
 
-	T* operator->() { return get(); }
+	// Returns true if we can dereference the pointer.
+	explicit operator bool() const noexcept { return block && block->pointed_to_object; }
+
+	// Retrieve the pointer to the observed object if we have one and it's still alive.
+	T* get() noexcept {
+		return block ? static_cast<T*>(block->pointed_to_object) : nullptr;
+	}
+	// Dereference the pointed to object.
+	T* operator->() noexcept { return static_cast<T*>(block->pointed_to_object); }
+
+	// Reset the pointer so that it does not point to anything.
+	// When the pointed to object and all observer pointers to it have been destroyed, it will deallocate the block.
+	void reset() noexcept
+	{
+		if (block)
+		{
+			block->num_observers -= 1;
+			DeallocateObserverPtrBlockIfEmpty(block);
+			block = nullptr;
+		}
+	}
 
 private:
-	WeakPtr<uintptr_t> ptr;
+
+	friend class EnableObserverPtr<T>;
+
+	explicit ObserverPtr(ObserverPtrBlock* block) noexcept : block(block)
+	{
+		if (block)
+			block->num_observers += 1;
+	}
+
+	ObserverPtrBlock* block;
 };
 
 
+
+
 template<typename T>
-class EnableObserverPtr {
+class RMLUICORE_API EnableObserverPtr {
 public:
-	ObserverPtr<T> GetObserverPtr() const { return ObserverPtr<T>(self_reference); }
+
+	ObserverPtr<T> GetObserverPtr() const noexcept { return ObserverPtr<T>(block); }
 
 protected:
-	EnableObserverPtr() : self_reference(std::make_shared<uintptr_t>(reinterpret_cast<uintptr_t>(static_cast<T*>(this))))
-	{}
+
+	EnableObserverPtr() noexcept
+	{
+		static_assert(std::is_base_of<EnableObserverPtr<T>, T>::value, "T must derive from EnableObserverPtr<T>.");
+		block = AllocateObserverPtrBlock();
+		block->pointed_to_object = static_cast<void*>(static_cast<T*>(this));
+	}
+
+	~EnableObserverPtr() noexcept 
+	{
+		block->pointed_to_object = nullptr;
+		DeallocateObserverPtrBlockIfEmpty(block);
+	}
 
 private:
-	SharedPtr<uintptr_t> self_reference;
-};
 
+	// The observer ptr block is assumed to be uniquely owned by us, copy operations must never be implemented.
+	EnableObserverPtr<T>(const EnableObserverPtr<T>&) = delete;
+	EnableObserverPtr<T>& operator=(const EnableObserverPtr<T>&) = delete;
+
+	// It's not clear what should happen to the block if we perform move operations. Probably a no-op? For now we don't need them.
+	EnableObserverPtr<T>(EnableObserverPtr<T>&& other) = delete;
+	EnableObserverPtr<T>& operator=(EnableObserverPtr<T>&&) = delete;
 
-struct ObserverPtrBlock {
-	int num_observer_pointers = 0;
-	uintptr_t pointed_to_object = 0;
+	ObserverPtrBlock* block;
 };
 
 
+
 }
 }
 

+ 19 - 0
Source/Core/ObserverPtr.cpp

@@ -28,10 +28,29 @@
 
 #include "precompiled.h"
 #include "../../Include/RmlUi/Core/ObserverPtr.h"
+#include "Pool.h"
 
 namespace Rml {
 namespace Core {
 
 
+static Pool< ObserverPtrBlock > observer_ptr_block_pool(400, true);
+
+
+void DeallocateObserverPtrBlockIfEmpty(ObserverPtrBlock* block) {
+	RMLUI_ASSERT(block->num_observers >= 0);
+	if (block->num_observers == 0 && block->pointed_to_object == nullptr)
+	{
+		observer_ptr_block_pool.DestroyAndDeallocate(block);
+	}
+}
+
+ObserverPtrBlock* AllocateObserverPtrBlock()
+{
+	return observer_ptr_block_pool.AllocateAndConstruct();
+}
+
+
+
 }
 }