Add new shared pointer and make use of it

This commit is contained in:
Gericom
2026-03-28 12:03:06 +01:00
parent bec797ffe7
commit 21a8790ebc
66 changed files with 1098 additions and 523 deletions

View File

@@ -0,0 +1,35 @@
#include "common.h"
#include <libtwl/rtos/rtosIrq.h>
#include "AtomicSharedPtr.h"
void AtomicSharedPtrBase::Reset(void* newObject, RefCount* newRefCount, bool increaseNewRefCount)
{
u32 irq = rtos_disableIrqs(); // 1
auto refCount = _refCount;
_object = newObject;
_refCount = newRefCount;
if (increaseNewRefCount && _refCount)
{
_refCount->refCount++;
}
if (refCount && --refCount->refCount == 0) [[gnu::unlikely]]
{
refCount->weakRefCount++; // ensure the ref count is not destructed elsewhere
rtos_restoreIrqs(irq); // 1
refCount->DestructObject();
irq = rtos_disableIrqs(); // 2
if (--refCount->weakRefCount == 0) [[gnu::unlikely]]
{
rtos_restoreIrqs(irq); // 2
delete refCount;
}
else
{
rtos_restoreIrqs(irq); // 2
}
}
else
{
rtos_restoreIrqs(irq); // 1
}
}

View File

@@ -0,0 +1,134 @@
#pragma once
#include <type_traits>
#include <libtwl/rtos/rtosIrq.h>
#include "SharedPtr.h"
class AtomicSharedPtrBase
{
public:
void Reset()
{
Reset(nullptr, nullptr, false);
}
protected:
void* volatile _object;
RefCount* volatile _refCount;
AtomicSharedPtrBase()
: _object(nullptr), _refCount(nullptr) { }
AtomicSharedPtrBase(void* object, RefCount* refCount)
: _object(object), _refCount(refCount) { }
~AtomicSharedPtrBase()
{
Reset(nullptr, nullptr, false);
}
void Reset(void* newObject, RefCount* newRefCount, bool increaseNewRefCount);
};
template <class T>
class AtomicSharedPtr : public AtomicSharedPtrBase
{
public:
AtomicSharedPtr() { }
AtomicSharedPtr(std::nullptr_t) { }
AtomicSharedPtr(const SharedPtr<T>& sharedPtr)
: AtomicSharedPtrBase(sharedPtr._object, sharedPtr._refCount)
{
if (_refCount)
{
shared_ptr_increase_ref_count(_refCount->refCount);
}
}
template <class Y> requires std::assignable_from<T*&, Y*>
AtomicSharedPtr(const SharedPtr<Y>& sharedPtr)
: AtomicSharedPtrBase(static_cast<T*>(sharedPtr.GetPointer()), sharedPtr._refCount)
{
if (_refCount)
{
shared_ptr_increase_ref_count(_refCount->refCount);
}
}
template <class Y>
explicit AtomicSharedPtr(const SharedPtr<Y>& sharedPtr)
: AtomicSharedPtrBase(static_cast<T*>(sharedPtr.GetPointer()), sharedPtr._refCount)
{
if (_refCount)
{
shared_ptr_increase_ref_count(_refCount->refCount);
}
}
AtomicSharedPtr(SharedPtr<T>&& sharedPtr)
: AtomicSharedPtrBase(sharedPtr._object, sharedPtr._refCount)
{
sharedPtr._object = nullptr;
sharedPtr._refCount = nullptr;
}
template <class Y> requires std::assignable_from<T*&, Y*>
AtomicSharedPtr(SharedPtr<Y>&& sharedPtr)
: AtomicSharedPtrBase(static_cast<T*>(sharedPtr.GetPointer()), sharedPtr._refCount)
{
sharedPtr._object = nullptr;
sharedPtr._refCount = nullptr;
}
template <class Y>
explicit AtomicSharedPtr(SharedPtr<Y>&& sharedPtr)
: AtomicSharedPtrBase(static_cast<T*>(sharedPtr.GetPointer()), sharedPtr._refCount)
{
sharedPtr._object = nullptr;
sharedPtr._refCount = nullptr;
}
AtomicSharedPtr& operator=(const SharedPtr<T>& sharedPtr)
{
Reset(sharedPtr._object, sharedPtr._refCount, true);
return *this;
}
template <class Y> requires std::assignable_from<T*&, Y*>
AtomicSharedPtr<T>& operator=(const SharedPtr<Y>& sharedPtr)
{
Reset(static_cast<T*>(sharedPtr.GetPointer()), sharedPtr._refCount, true);
return *this;
}
AtomicSharedPtr& operator=(SharedPtr<T>&& sharedPtr)
{
Reset(sharedPtr._object, sharedPtr._refCount, false);
sharedPtr._object = nullptr;
sharedPtr._refCount = nullptr;
return *this;
}
template <class Y> requires std::assignable_from<T*&, Y*>
AtomicSharedPtr<T>& operator=(SharedPtr<Y>&& sharedPtr)
{
Reset(static_cast<T*>(sharedPtr.GetPointer()), sharedPtr._refCount, false);
sharedPtr._object = nullptr;
sharedPtr._refCount = nullptr;
return *this;
}
SharedPtr<T> Lock() const
{
u32 irq = rtos_disableIrqs();
auto object = static_cast<T*>(_object);
auto refCount = _refCount;
if (refCount)
{
refCount->refCount++;
}
rtos_restoreIrqs(irq);
return SharedPtr<T>(object, refCount);
}
};

View File

@@ -0,0 +1,42 @@
#pragma once
#include "SharedPtr.h"
#include "WeakPtr.h"
class EnableSharedFromThisBase
{
template <class Y>
friend class SharedPtr;
template <class Y>
friend class EnableSharedFromThis;
private:
EnableSharedFromThisBase() = default;
};
template <class T>
class EnableSharedFromThis : public EnableSharedFromThisBase
{
template <class Y>
friend class SharedPtr;
protected:
SharedPtr<T> SharedFromThis()
{
return __sharedFromThisWeakPtr.Lock();
}
WeakPtr<T> WeakFromThis()
{
return __sharedFromThisWeakPtr;
}
private:
WeakPtr<T> __sharedFromThisWeakPtr;
template <class Y>
void __SetSharedFromThisWeakPtr(const SharedPtr<Y>& sharedPtr)
{
__sharedFromThisWeakPtr = WeakPtr<Y>(sharedPtr.GetPointer(), sharedPtr._refCount);
}
};

View File

@@ -0,0 +1,58 @@
#pragma once
#include <array>
extern "C" void shared_ptr_increase_ref_count(vu32& refCount);
class RefCount
{
public:
vu32 refCount;
vu32 weakRefCount;
virtual ~RefCount() = default;
virtual void DestructObject() = 0;
protected:
explicit RefCount()
: refCount(1), weakRefCount(0) { }
};
template <class T>
class StandaloneRefCount : public RefCount
{
public:
explicit StandaloneRefCount(T* object)
: _object(object) { }
void DestructObject() final
{
delete _object;
}
private:
T* _object;
};
template <class T>
class alignas(T) MakeSharedRefCount : public RefCount
{
public:
explicit MakeSharedRefCount(auto&&... args)
{
new (_object.data()) T(std::forward<decltype(args)>(args)...);
}
T* GetObject()
{
return reinterpret_cast<T*>(_object.data());
}
void DestructObject() final
{
reinterpret_cast<T*>(_object.data())->~T();
}
private:
std::array<u8, sizeof(T)> _object alignas(T);
};

View File

@@ -0,0 +1,31 @@
#include "common.h"
#include <libtwl/rtos/rtosIrq.h>
#include "SharedPtr.h"
void SharedPtrBase::ResetIntern()
{
auto refCount = _refCount;
_object = nullptr;
_refCount = nullptr;
u32 irq = rtos_disableIrqs(); // 1
if (--refCount->refCount == 0) [[gnu::unlikely]]
{
refCount->weakRefCount++; // ensure the ref count is not destructed elsewhere
rtos_restoreIrqs(irq); // 1
refCount->DestructObject();
irq = rtos_disableIrqs(); // 2
if (--refCount->weakRefCount == 0) [[gnu::unlikely]]
{
rtos_restoreIrqs(irq); // 2
delete refCount;
}
else
{
rtos_restoreIrqs(irq); // 2
}
}
else
{
rtos_restoreIrqs(irq); // 1
}
}

View File

@@ -1,163 +1,209 @@
#pragma once
#include <type_traits>
#include "RefCount.h"
static inline u32 arm_getCpsr()
{
u32 cpsr;
asm volatile("mrs %0, cpsr" : "=r" (cpsr));
return cpsr;
}
static inline void arm_setCpsrControl(u32 cpsrControl)
{
asm volatile("msr cpsr_c, %0" :: "r" (cpsrControl) : "cc");
}
static inline u32 arm_disableIrqs(void)
{
u32 oldCpsr = arm_getCpsr();
arm_setCpsrControl(oldCpsr | 0x80);
return oldCpsr;
}
static inline void arm_restoreIrqs(u32 oldCpsr)
{
arm_setCpsrControl(oldCpsr);
}
class EnableSharedFromThisBase;
template <class T>
class SharedPtr
class EnableSharedFromThis;
class SharedPtrBase
{
T* _pointer;
vu32* _refCount;
public:
SharedPtr()
: _pointer(nullptr), _refCount(nullptr) { (void)sizeof(T); }
explicit SharedPtr(T* pointer)
: _pointer(pointer), _refCount(pointer ? new u32(1) : nullptr) { (void)sizeof(T); }
SharedPtr(const SharedPtr& other)
void Reset()
{
u32 irq = arm_disableIrqs();
_pointer = other._pointer;
_refCount = other._refCount;
if (_pointer)
if (_refCount != nullptr)
{
(*_refCount)++;
ResetIntern();
}
arm_restoreIrqs(irq);
}
SharedPtr(SharedPtr&& other)
: _pointer(other._pointer), _refCount(other._refCount)
{
other._pointer = nullptr;
other._refCount = nullptr;
}
protected:
void* _object;
RefCount* _refCount;
~SharedPtr()
SharedPtrBase()
: _object(nullptr), _refCount(nullptr) { }
SharedPtrBase(void* object, RefCount* refCount)
: _object(object), _refCount(refCount) { }
SharedPtrBase(const void* object, RefCount* refCount)
: _object((void*)object), _refCount(refCount) { }
~SharedPtrBase()
{
Reset();
}
[[gnu::noinline]]
void ResetIntern();
};
template <class T>
class SharedPtr : public SharedPtrBase
{
template <class Y> friend class WeakPtr;
template <class Y> friend class SharedPtr;
template <class Y> friend class AtomicSharedPtr;
template <class Y> friend class EnableSharedFromThis;
public:
SharedPtr() { }
SharedPtr(std::nullptr_t) { }
explicit SharedPtr(T* object)
: SharedPtrBase(object, object == nullptr ? nullptr : new StandaloneRefCount<T>(object))
{
if (_object != nullptr)
{
if constexpr (std::is_convertible<T*, EnableSharedFromThisBase*>::value)
{
_refCount->weakRefCount = 1;
GetPointer()->__SetSharedFromThisWeakPtr(*this);
}
}
}
template <class Y> requires std::assignable_from<T*&, Y*>
explicit SharedPtr(Y* object)
: SharedPtrBase(static_cast<T*>(object), object == nullptr ? nullptr : new StandaloneRefCount<Y>(object))
{
if (object != nullptr)
{
if constexpr (std::is_convertible<Y*, EnableSharedFromThisBase*>::value)
{
_refCount->weakRefCount = 1;
GetPointer()->__SetSharedFromThisWeakPtr(*this);
}
}
}
SharedPtr(const SharedPtr& other)
: SharedPtrBase(other._object, other._refCount)
{
if (_refCount)
{
shared_ptr_increase_ref_count(_refCount->refCount);
}
}
template <class Y> requires std::assignable_from<T*&, Y*>
SharedPtr(const SharedPtr<Y>& other)
: SharedPtrBase(static_cast<T*>(other.GetPointer()), other._refCount)
{
if (_refCount)
{
shared_ptr_increase_ref_count(_refCount->refCount);
}
}
template <class Y>
explicit SharedPtr(const SharedPtr<Y>& other)
: SharedPtrBase(static_cast<T*>(other.GetPointer()), other._refCount)
{
if (_refCount)
{
shared_ptr_increase_ref_count(_refCount->refCount);
}
}
SharedPtr(SharedPtr&& other)
: SharedPtrBase(other._object, other._refCount)
{
other._object = nullptr;
other._refCount = nullptr;
}
template <class Y> requires std::assignable_from<T*&, Y*>
SharedPtr(SharedPtr<Y>&& other)
: SharedPtrBase(static_cast<T*>(other.GetPointer()), other._refCount)
{
other._object = nullptr;
other._refCount = nullptr;
}
template <class Y>
explicit SharedPtr(SharedPtr<Y>&& other)
: SharedPtrBase(static_cast<T*>(other.GetPointer()), other._refCount)
{
other._object = nullptr;
other._refCount = nullptr;
}
static SharedPtr<T> MakeShared(auto&&... args)
{
auto refCount = new MakeSharedRefCount<T>(std::forward<decltype(args)>(args)...);
return SharedPtr<T>(refCount->GetObject(), refCount, true);
}
SharedPtr& operator=(const SharedPtr& other)
{
u32 irq = arm_disableIrqs();
T* pointer = _pointer;
if (pointer)
Reset();
_object = other._object;
_refCount = other._refCount;
if (_refCount)
{
vu32* refCount = _refCount;
u32 newValue = *refCount - 1;
*refCount = newValue;
_pointer = other._pointer;
_refCount = other._refCount;
if (_pointer)
{
(*_refCount)++;
}
arm_restoreIrqs(irq);
if (newValue == 0)
{
delete pointer;
delete refCount;
}
}
else
{
_pointer = other._pointer;
_refCount = other._refCount;
if (_pointer)
{
(*_refCount)++;
}
arm_restoreIrqs(irq);
shared_ptr_increase_ref_count(_refCount->refCount);
}
return *this;
}
template <class Y> requires std::assignable_from<T*&, Y*>
SharedPtr<T>& operator=(const SharedPtr<Y>& other)
{
Reset();
_object = static_cast<T*>(other.GetPointer());
_refCount = other._refCount;
if (_refCount)
{
shared_ptr_increase_ref_count(_refCount->refCount);
}
return *this;
}
[[gnu::noinline]]
SharedPtr& operator=(SharedPtr&& other)
{
u32 irq = arm_disableIrqs();
T* pointer = _pointer;
if (pointer)
{
vu32* refCount = _refCount;
u32 newValue = *refCount - 1;
*refCount = newValue;
_pointer = other._pointer;
_refCount = other._refCount;
other._pointer = nullptr;
other._refCount = nullptr;
arm_restoreIrqs(irq);
if (newValue == 0)
{
delete pointer;
delete refCount;
}
}
else
{
_pointer = other._pointer;
_refCount = other._refCount;
other._pointer = nullptr;
other._refCount = nullptr;
arm_restoreIrqs(irq);
}
Reset();
_object = other._object;
_refCount = other._refCount;
other._object = nullptr;
other._refCount = nullptr;
return *this;
}
[[gnu::noinline]]
void Reset()
template <class Y> requires std::assignable_from<T*&, Y*>
SharedPtr<T>& operator=(SharedPtr<Y>&& other)
{
u32 irq = arm_disableIrqs();
T* pointer = _pointer;
if (pointer)
{
vu32* refCount = _refCount;
u32 newValue = *refCount - 1;
*refCount = newValue;
_pointer = nullptr;
_refCount = nullptr;
arm_restoreIrqs(irq);
if (newValue == 0)
{
delete pointer;
delete refCount;
}
}
else
{
arm_restoreIrqs(irq);
}
Reset();
_object = static_cast<T*>(other.GetPointer());
_refCount = other._refCount;
other._object = nullptr;
other._refCount = nullptr;
return *this;
}
constexpr T& operator*() const { return *_pointer; }
constexpr T* operator->() const { return _pointer; }
T& operator*() const { return *static_cast<T*>(_object); }
T* operator->() const { return static_cast<T*>(_object); }
T* GetPointer() const { return static_cast<T*>(_object); }
constexpr T* GetPointer() const { return _pointer; }
constexpr u32 GetRefCount() const { return _refCount ? *_refCount : 0; }
constexpr bool IsValid() const { return _pointer; }
bool IsValid() const { return _object != nullptr; }
operator bool() const { return _object != nullptr; }
private:
SharedPtr(T* object, RefCount* refCount)
: SharedPtrBase(object, refCount) { }
SharedPtr(T* object, RefCount* refCount, bool doSharedFromThis)
: SharedPtrBase(object, refCount)
{
if (doSharedFromThis)
{
if constexpr (std::is_convertible<T*, EnableSharedFromThisBase*>::value)
{
_refCount->weakRefCount = 1;
GetPointer()->__SetSharedFromThisWeakPtr(*this);
}
}
}
};

View File

@@ -0,0 +1,15 @@
.section .itcm
.arm
// r0 = &refCount
.global shared_ptr_increase_ref_count
.type shared_ptr_increase_ref_count, %function
shared_ptr_increase_ref_count:
mrs r2, cpsr
orr r1, r2, #0x80
msr cpsr_c, r1
ldr r12, [r0]
add r12, r12, #1
str r12, [r0]
msr cpsr_c, r2
bx lr

View File

@@ -0,0 +1,35 @@
#include "common.h"
#include <libtwl/rtos/rtosIrq.h>
#include "WeakPtr.h"
void WeakPtrBase::ResetIntern()
{
u32 irq = rtos_disableIrqs();
auto refCount = _refCount;
if (--refCount->weakRefCount == 0)
{
_refCount = nullptr;
rtos_restoreIrqs(irq);
delete refCount;
}
else
{
rtos_restoreIrqs(irq);
}
}
bool WeakPtrBase::LockIntern() const
{
u32 irq = rtos_disableIrqs();
if (_refCount->refCount != 0)
{
_refCount->refCount++;
rtos_restoreIrqs(irq);
return true;
}
else
{
rtos_restoreIrqs(irq);
return false;
}
}

158
arm9/source/core/WeakPtr.h Normal file
View File

@@ -0,0 +1,158 @@
#pragma once
#include <type_traits>
#include "RefCount.h"
#include "SharedPtr.h"
class WeakPtrBase
{
public:
~WeakPtrBase()
{
Reset();
}
void Reset()
{
if (_refCount)
{
Reset();
}
}
protected:
RefCount* _refCount;
WeakPtrBase()
: _refCount(nullptr) { }
explicit WeakPtrBase(RefCount* refCount)
: _refCount(refCount) { }
void ResetIntern();
bool LockIntern() const;
};
template <class T>
class WeakPtr : public WeakPtrBase
{
template <class Y> friend class WeakPtr;
template <class Y> friend class EnableSharedFromThis;
public:
WeakPtr() { }
WeakPtr(std::nullptr_t) { }
WeakPtr(const WeakPtr& other)
: WeakPtrBase(other._refCount), _object(other._object)
{
if (_refCount)
{
shared_ptr_increase_ref_count(_refCount->weakRefCount);
}
}
WeakPtr(WeakPtr&& other)
: WeakPtrBase(other._refCount), _object(other._object)
{
other._refCount = nullptr;
other._object = nullptr;
}
template <class Y> requires std::assignable_from<T*&, Y*>
WeakPtr(const SharedPtr<Y>& sharedPtr)
: WeakPtrBase(sharedPtr._refCount), _object(static_cast<T*>(sharedPtr.GetPointer()))
{
if (_refCount)
{
shared_ptr_increase_ref_count(_refCount->weakRefCount);
}
}
template <class Y>
explicit WeakPtr(const SharedPtr<Y>& sharedPtr)
: WeakPtrBase(sharedPtr._refCount), _object(static_cast<T*>(sharedPtr.GetPointer()))
{
if (_refCount)
{
shared_ptr_increase_ref_count(_refCount->weakRefCount);
}
}
WeakPtr& operator=(const WeakPtr& other)
{
Reset();
_object = other._object;
_refCount = other._refCount;
if (_refCount)
{
shared_ptr_increase_ref_count(_refCount->weakRefCount);
}
return *this;
}
template <class Y> requires std::assignable_from<T*&, Y*>
WeakPtr<T>& operator=(const WeakPtr<Y>& other)
{
Reset();
_object = static_cast<T*>(static_cast<Y*>(other._object));
_refCount = other._refCount;
if (_refCount)
{
shared_ptr_increase_ref_count(_refCount->weakRefCount);
}
return *this;
}
template <class Y> requires std::assignable_from<T*&, Y*>
WeakPtr<T>& operator=(const SharedPtr<Y>& sharedPtr)
{
Reset();
_object = static_cast<T*>(sharedPtr.GetPointer());
_refCount = sharedPtr._refCount;
if (_refCount)
{
shared_ptr_increase_ref_count(_refCount->weakRefCount);
}
return *this;
}
WeakPtr& operator=(WeakPtr&& other)
{
Reset();
_object = other._object;
_refCount = other._refCount;
other._object = nullptr;
other._refCount = nullptr;
return *this;
}
template <class Y> requires std::assignable_from<T*&, Y*>
WeakPtr<T>& operator=(WeakPtr<Y>&& other)
{
Reset();
_object = static_cast<T*>(static_cast<Y*>(other._object));
_refCount = other._refCount;
other._object = nullptr;
other._refCount = nullptr;
return *this;
}
SharedPtr<T> Lock() const
{
if (_refCount && LockIntern())
{
return SharedPtr<T>(_object, _refCount);
}
else
{
return SharedPtr<T>();
}
}
private:
T* _object;
WeakPtr(T* object, RefCount* refCount)
: WeakPtrBase(refCount), _object(object) { }
};