View Issue Details

IDProjectCategoryView StatusLast Update
0001232NoesisGUIC++ SDKpublic2022-11-02 11:59
Reporternikobarli Assigned Tojsantos  
PrioritynormalSeverityfeature 
Status assignedResolutionopen 
Product Version2.1.0rc2 
Summary0001232: Weak pointer support
Description

This is follow up for the meeting with Jesus on Feb 1, 2018.

We feel that we sometimes need weak pointer to manage resources. Please consider adding this feature back in the future.

PlatformAny

Activities

jsantos

jsantos

2022-10-24 12:51

manager   ~0008105

Last edited: 2022-10-24 12:51

I am attaching a potential implementation for weak pointers in Noesis. We have plans to officially implement this in the future although we need more discussions.

BaseRefCounted.cpp.patch (2,211 bytes)   
Index: BaseRefCounted.cpp
===================================================================
--- BaseRefCounted.cpp	(revision 11648)
+++ BaseRefCounted.cpp	(working copy)
@@ -6,12 +6,72 @@
 
 #include <NsCore/BaseRefCounted.h>
 #include <NsCore/ReflectionImplementEmpty.h>
+#include <NsCore/HashMap.h>
+#include <NsCore/Ptr.h>
+#include <NsCore/SpinMutex.h>
 
 
 using namespace Noesis;
 
 
+static SpinMutex gHandlesMutex;
+static AtomicInteger gHandlesCounter;
+static HashMap<uint32_t, BaseRefCounted*> gHandlesMap;
+
+
 ////////////////////////////////////////////////////////////////////////////////////////////////////
+BaseRefCounted::~BaseRefCounted()
+{
+    NS_CHECK([&]()
+    {
+        // Note that 1 is valid when the object lives is the stack or is being manually destroyed
+        int32_t n = mRefCount.Load();
+        NS_CHECK(n == 1 || n == 0, "Unexpected RefCount(%d) deleting object at %p", n, this);
+        return true;
+    }
+    (), "");
+
+    if (mHandle != 0)
+    {
+        SpinMutex::ScopedLock lock(gHandlesMutex);
+        gHandlesMap.Erase(mHandle);
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+uint32_t BaseRefCounted::GetHandle()
+{
+    if (mHandle == 0)
+    {
+        mHandle = gHandlesCounter.IncrementAndFetch();
+
+        SpinMutex::ScopedLock lock(gHandlesMutex);
+        gHandlesMap.Insert(mHandle, this);
+    }
+
+    return mHandle;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+Ptr<BaseRefCounted> BaseRefCounted::FromHandle(uint32_t handle)
+{
+    SpinMutex::ScopedLock lock(gHandlesMutex);
+    auto it = gHandlesMap.Find(handle);
+
+    if (it != gHandlesMap.End())
+    {
+        uint32_t refs = it->value->mRefCount.IncrementAndFetch();
+
+        if (refs > 1)
+        {
+            return Ptr<BaseRefCounted>(*it->value);
+        }
+    }
+
+    return nullptr;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
 NS_BEGIN_COLD_REGION
 
 NS_IMPLEMENT_REFLECTION_(BaseRefCounted)
BaseRefCounted.cpp.patch (2,211 bytes)   
BaseRefCounted.h.patch (1,197 bytes)   
Index: BaseRefCounted.h
===================================================================
--- BaseRefCounted.h	(revision 11648)
+++ BaseRefCounted.h	(working copy)
@@ -28,6 +28,7 @@
     #define NS_BASEREFCOUNTED_OVERRIDABLE
 #endif
 
+template<class T> class Ptr;
 
 ////////////////////////////////////////////////////////////////////////////////////////////////////
 /// Base class for types that control their lifetime with a reference counter. Instances of this
@@ -65,6 +66,12 @@
     /// Gets current reference count for the object
     int32_t GetNumReferences() const;
 
+    /// Returns a handle for the object. Handles are weak references
+    uint32_t GetHandle();
+
+    /// Retrieves the object referenced by the handle or nullptr if it was already destroyed
+    static Ptr<BaseRefCounted> FromHandle(uint32_t handle);
+
 protected:
     /// Invoked when the reference counter reaches 0. By default the instance is deleted
     /// Can be reimplemented by child classes to avoid the default destruction
@@ -73,6 +80,7 @@
 
 private:
     AtomicInteger mRefCount;
+    uint32_t mHandle;
 
     NS_DECLARE_REFLECTION(BaseRefCounted, BaseObject)
 };
BaseRefCounted.h.patch (1,197 bytes)   
BaseRefCounted.inl.patch (1,074 bytes)   
Index: BaseRefCounted.inl
===================================================================
--- BaseRefCounted.inl	(revision 11648)
+++ BaseRefCounted.inl	(working copy)
@@ -12,19 +12,11 @@
 
 
 ////////////////////////////////////////////////////////////////////////////////////////////////////
-inline BaseRefCounted::BaseRefCounted(): mRefCount(1)
+inline BaseRefCounted::BaseRefCounted(): mRefCount(1), mHandle(0)
 {
 }
 
 ////////////////////////////////////////////////////////////////////////////////////////////////////
-inline BaseRefCounted::~BaseRefCounted()
-{
-    // Note that 1 is valid when the object lives is the stack or is being manually destroyed
-    int32_t refs = mRefCount.Load();
-    NS_CHECK(refs == 1 || refs == 0, "Unexpected RefCount(%d) deleting object at %p", refs, this);
-}
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
 inline int32_t BaseRefCounted::AddReference() const
 {
     return const_cast<BaseRefCounted*>(this)->mRefCount.IncrementAndFetch();
BaseRefCounted.inl.patch (1,074 bytes)   
satorp

satorp

2022-10-26 07:01

reporter   ~0008106

The patch looks promising. There is a concern in the way the handles are generated, which seems to be done by simply incrementing a global counter (int32) without reuse. In case when application up time is long and handle generation occurs frequently, this could possibly lead to an overflow (duplicated handles).

jsantos

jsantos

2022-11-02 11:59

manager   ~0008110

You are right about that potential handle collision. The alternative could be using 64-bit handles and user the pointer itself as handle. But that is also dangerous.

And alternative is using the 32 bits for a global counter and the lower 32 bits filled with a hash of the pointer. With this, the chances of collision are almost impossible.

Issue History

Date Modified Username Field Change
2018-02-06 05:16 nikobarli New Issue
2018-11-01 02:14 jsantos View Status public => private
2018-11-22 09:36 sfernandez Assigned To => jsantos
2018-11-22 09:36 sfernandez Status new => assigned
2018-11-22 09:36 sfernandez View Status private => public
2018-11-22 09:36 sfernandez Platform => Any
2022-10-24 12:51 jsantos Note Added: 0008105
2022-10-24 12:51 jsantos File Added: BaseRefCounted.cpp.patch
2022-10-24 12:51 jsantos File Added: BaseRefCounted.h.patch
2022-10-24 12:51 jsantos File Added: BaseRefCounted.inl.patch
2022-10-24 12:51 jsantos Note Edited: 0008105
2022-10-24 12:51 jsantos Note Edited: 0008105
2022-10-26 07:01 satorp Note Added: 0008106
2022-11-02 11:59 jsantos Note Added: 0008110