mirror of
https://github.com/hyprwm/hyprutils.git
synced 2025-12-20 17:30:05 +01:00
memory: make SP/ASP control blocks type agnostic
This commit is contained in:
parent
94cce79434
commit
2d89cc1d0d
5 changed files with 128 additions and 94 deletions
|
|
@ -23,12 +23,11 @@
|
||||||
|
|
||||||
namespace Hyprutils::Memory {
|
namespace Hyprutils::Memory {
|
||||||
namespace Atomic_ {
|
namespace Atomic_ {
|
||||||
template <typename T>
|
class impl : public Impl_::impl_base {
|
||||||
class impl : public Impl_::impl<T> {
|
|
||||||
std::recursive_mutex m_mutex;
|
std::recursive_mutex m_mutex;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
impl(T* data, bool lock = true) noexcept : Impl_::impl<T>(data, lock) {
|
impl(void* data, DeleteFn deleter) noexcept : Impl_::impl_base(data, deleter, true) {
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -55,7 +54,13 @@ namespace Hyprutils::Memory {
|
||||||
using validHierarchy = std::enable_if_t<std::is_assignable_v<CAtomicSharedPointer<T>&, X>, CAtomicSharedPointer&>;
|
using validHierarchy = std::enable_if_t<std::is_assignable_v<CAtomicSharedPointer<T>&, X>, CAtomicSharedPointer&>;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit CAtomicSharedPointer(Impl_::impl_base* impl) noexcept : m_ptr(impl) {}
|
explicit CAtomicSharedPointer(T* object) noexcept : m_ptr(new Atomic_::impl(sc<void*>(object), _delete)) {
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
CAtomicSharedPointer(Impl_::impl_base* impl) noexcept : m_ptr(impl) {
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
CAtomicSharedPointer(const CAtomicSharedPointer<T>& ref) {
|
CAtomicSharedPointer(const CAtomicSharedPointer<T>& ref) {
|
||||||
if (!ref.m_ptr.impl_)
|
if (!ref.m_ptr.impl_)
|
||||||
|
|
@ -141,7 +146,7 @@ namespace Hyprutils::Memory {
|
||||||
// -> must unlock BEFORE reset
|
// -> must unlock BEFORE reset
|
||||||
// not last ref?
|
// not last ref?
|
||||||
// -> must unlock AFTER reset
|
// -> must unlock AFTER reset
|
||||||
auto& mutex = sc<Atomic_::impl<T>*>(m_ptr.impl_)->getMutex();
|
auto& mutex = sc<Atomic_::impl*>(m_ptr.impl_)->getMutex();
|
||||||
mutex.lock();
|
mutex.lock();
|
||||||
|
|
||||||
if (m_ptr.impl_->ref() > 1) {
|
if (m_ptr.impl_->ref() > 1) {
|
||||||
|
|
@ -152,7 +157,11 @@ namespace Hyprutils::Memory {
|
||||||
|
|
||||||
if (m_ptr.impl_->wref() == 0) {
|
if (m_ptr.impl_->wref() == 0) {
|
||||||
mutex.unlock(); // Don't hold the mutex when destroying it
|
mutex.unlock(); // Don't hold the mutex when destroying it
|
||||||
m_ptr.reset();
|
|
||||||
|
m_ptr.impl_->destroy();
|
||||||
|
delete sc<Atomic_::impl*>(m_ptr.impl_);
|
||||||
|
m_ptr.impl_ = nullptr;
|
||||||
|
|
||||||
// mutex invalid
|
// mutex invalid
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -163,12 +172,18 @@ namespace Hyprutils::Memory {
|
||||||
// To avoid this altogether, keep a weak pointer here.
|
// To avoid this altogether, keep a weak pointer here.
|
||||||
// This guarantees that impl_ is still valid after the reset.
|
// This guarantees that impl_ is still valid after the reset.
|
||||||
CWeakPointer<T> guard = m_ptr;
|
CWeakPointer<T> guard = m_ptr;
|
||||||
m_ptr.reset();
|
m_ptr.reset(); // destroys the data
|
||||||
|
|
||||||
// Now we can safely check if guard is the last wref.
|
// Now we can safely check if guard is the last wref.
|
||||||
if (guard.impl_->wref() == 1) {
|
if (guard.impl_->wref() == 1) {
|
||||||
mutex.unlock();
|
mutex.unlock();
|
||||||
return; // ~guard destroys impl_ and mutex
|
|
||||||
|
// destroy impl_ (includes the mutex)
|
||||||
|
delete sc<Atomic_::impl*>(guard.impl_);
|
||||||
|
guard.impl_ = nullptr;
|
||||||
|
|
||||||
|
// mutex invalid
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
guard.reset();
|
guard.reset();
|
||||||
|
|
@ -205,8 +220,12 @@ namespace Hyprutils::Memory {
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
static void _delete(void* p) {
|
||||||
|
std::default_delete<T>{}(sc<T*>(p));
|
||||||
|
}
|
||||||
|
|
||||||
std::lock_guard<std::recursive_mutex> implLockGuard() const {
|
std::lock_guard<std::recursive_mutex> implLockGuard() const {
|
||||||
return sc<Atomic_::impl<T>*>(m_ptr.impl_)->lockGuard();
|
return sc<Atomic_::impl*>(m_ptr.impl_)->lockGuard();
|
||||||
}
|
}
|
||||||
|
|
||||||
CSharedPointer<T> m_ptr;
|
CSharedPointer<T> m_ptr;
|
||||||
|
|
@ -312,11 +331,13 @@ namespace Hyprutils::Memory {
|
||||||
// -> must unlock BEFORE reset
|
// -> must unlock BEFORE reset
|
||||||
// not last ref?
|
// not last ref?
|
||||||
// -> must unlock AFTER reset
|
// -> must unlock AFTER reset
|
||||||
auto& mutex = sc<Atomic_::impl<T>*>(m_ptr.impl_)->getMutex();
|
auto& mutex = sc<Atomic_::impl*>(m_ptr.impl_)->getMutex();
|
||||||
mutex.lock();
|
mutex.lock();
|
||||||
if (m_ptr.impl_->ref() == 0 && m_ptr.impl_->wref() == 1) {
|
if (m_ptr.impl_->ref() == 0 && m_ptr.impl_->wref() == 1) {
|
||||||
mutex.unlock();
|
mutex.unlock();
|
||||||
m_ptr.reset();
|
|
||||||
|
delete sc<Atomic_::impl*>(m_ptr.impl_);
|
||||||
|
m_ptr.impl_ = nullptr;
|
||||||
// mutex invalid
|
// mutex invalid
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -375,7 +396,7 @@ namespace Hyprutils::Memory {
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::lock_guard<std::recursive_mutex> implLockGuard() const {
|
std::lock_guard<std::recursive_mutex> implLockGuard() const {
|
||||||
return sc<Atomic_::impl<T>*>(m_ptr.impl_)->lockGuard();
|
return sc<Atomic_::impl*>(m_ptr.impl_)->lockGuard();
|
||||||
}
|
}
|
||||||
|
|
||||||
CWeakPointer<T> m_ptr;
|
CWeakPointer<T> m_ptr;
|
||||||
|
|
@ -387,7 +408,7 @@ namespace Hyprutils::Memory {
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename U, typename... Args>
|
template <typename U, typename... Args>
|
||||||
static CAtomicSharedPointer<U> makeAtomicShared(Args&&... args) {
|
[[nodiscard]] inline CAtomicSharedPointer<U> makeAtomicShared(Args&&... args) {
|
||||||
return CAtomicSharedPointer<U>(new Atomic_::impl<U>(new U(std::forward<Args>(args)...)));
|
return CAtomicSharedPointer<U>(new U(std::forward<Args>(args)...));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,28 +8,61 @@ namespace Hyprutils {
|
||||||
namespace Impl_ {
|
namespace Impl_ {
|
||||||
class impl_base {
|
class impl_base {
|
||||||
public:
|
public:
|
||||||
virtual ~impl_base() = default;
|
using DeleteFn = void (*)(void*);
|
||||||
|
|
||||||
virtual void inc() noexcept = 0;
|
impl_base(void* data, DeleteFn deleter, bool lock = false) noexcept : _lockable(lock), _data(data), _deleter(deleter) {
|
||||||
virtual void dec() noexcept = 0;
|
|
||||||
virtual void incWeak() noexcept = 0;
|
|
||||||
virtual void decWeak() noexcept = 0;
|
|
||||||
virtual unsigned int ref() noexcept = 0;
|
|
||||||
virtual unsigned int wref() noexcept = 0;
|
|
||||||
virtual void destroy() noexcept = 0;
|
|
||||||
virtual bool destroying() noexcept = 0;
|
|
||||||
virtual bool dataNonNull() noexcept = 0;
|
|
||||||
virtual bool lockable() noexcept = 0;
|
|
||||||
virtual void* getData() noexcept = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
class impl : public impl_base {
|
|
||||||
public:
|
|
||||||
impl(T* data, bool lock = true) noexcept : _lockable(lock), _data(data) {
|
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void inc() noexcept {
|
||||||
|
_ref++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void dec() noexcept {
|
||||||
|
_ref--;
|
||||||
|
}
|
||||||
|
|
||||||
|
void incWeak() noexcept {
|
||||||
|
_weak++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void decWeak() noexcept {
|
||||||
|
_weak--;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int ref() noexcept {
|
||||||
|
return _ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int wref() noexcept {
|
||||||
|
return _weak;
|
||||||
|
}
|
||||||
|
|
||||||
|
void destroy() noexcept {
|
||||||
|
_destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool destroying() noexcept {
|
||||||
|
return _destroying;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool lockable() noexcept {
|
||||||
|
return _lockable;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool dataNonNull() noexcept {
|
||||||
|
return _data != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* getData() noexcept {
|
||||||
|
return _data;
|
||||||
|
}
|
||||||
|
|
||||||
|
~impl_base() {
|
||||||
|
destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
/* strong refcount */
|
/* strong refcount */
|
||||||
unsigned int _ref = 0;
|
unsigned int _ref = 0;
|
||||||
/* weak refcount */
|
/* weak refcount */
|
||||||
|
|
@ -37,13 +70,7 @@ namespace Hyprutils {
|
||||||
/* if this is lockable (shared) */
|
/* if this is lockable (shared) */
|
||||||
bool _lockable = true;
|
bool _lockable = true;
|
||||||
|
|
||||||
T* _data = nullptr;
|
void* _data = nullptr;
|
||||||
|
|
||||||
friend void swap(impl*& a, impl*& b) {
|
|
||||||
impl* tmp = a;
|
|
||||||
a = b;
|
|
||||||
b = tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* if the destructor was called,
|
/* if the destructor was called,
|
||||||
creating shared_ptrs is no longer valid */
|
creating shared_ptrs is no longer valid */
|
||||||
|
|
@ -63,56 +90,7 @@ namespace Hyprutils {
|
||||||
_destroying = false;
|
_destroying = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::default_delete<T> _deleter{};
|
DeleteFn _deleter = nullptr;
|
||||||
|
|
||||||
//
|
|
||||||
virtual void inc() noexcept {
|
|
||||||
_ref++;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void dec() noexcept {
|
|
||||||
_ref--;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void incWeak() noexcept {
|
|
||||||
_weak++;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void decWeak() noexcept {
|
|
||||||
_weak--;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual unsigned int ref() noexcept {
|
|
||||||
return _ref;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual unsigned int wref() noexcept {
|
|
||||||
return _weak;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void destroy() noexcept {
|
|
||||||
_destroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual bool destroying() noexcept {
|
|
||||||
return _destroying;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual bool lockable() noexcept {
|
|
||||||
return _lockable;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual bool dataNonNull() noexcept {
|
|
||||||
return _data != nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void* getData() noexcept {
|
|
||||||
return _data;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual ~impl() {
|
|
||||||
destroy();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ namespace Hyprutils {
|
||||||
|
|
||||||
/* creates a new shared pointer managing a resource
|
/* creates a new shared pointer managing a resource
|
||||||
avoid calling. Could duplicate ownership. Prefer makeShared */
|
avoid calling. Could duplicate ownership. Prefer makeShared */
|
||||||
explicit CSharedPointer(T* object) noexcept : impl_(new Impl_::impl<T>(object)) {
|
explicit CSharedPointer(T* object) noexcept : impl_(new Impl_::impl_base(sc<void*>(object), _delete)) {
|
||||||
increment();
|
increment();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -140,6 +140,10 @@ namespace Hyprutils {
|
||||||
Impl_::impl_base* impl_ = nullptr;
|
Impl_::impl_base* impl_ = nullptr;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
static void _delete(void* p) {
|
||||||
|
std::default_delete<T>{}(sc<T*>(p));
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
no-op if there is no impl_
|
no-op if there is no impl_
|
||||||
may delete the stored object if ref == 0
|
may delete the stored object if ref == 0
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ namespace Hyprutils {
|
||||||
|
|
||||||
/* creates a new unique pointer managing a resource
|
/* creates a new unique pointer managing a resource
|
||||||
avoid calling. Could duplicate ownership. Prefer makeUnique */
|
avoid calling. Could duplicate ownership. Prefer makeUnique */
|
||||||
explicit CUniquePointer(T* object) noexcept : impl_(new Impl_::impl<T>(object, false)) {
|
explicit CUniquePointer(T* object) noexcept : impl_(new Impl_::impl_base(sc<void*>(object), [](void* p) { std::default_delete<T>{}(sc<T*>(p)); }, false)) {
|
||||||
increment();
|
increment();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -92,6 +92,38 @@ static int testAtomicImpl() {
|
||||||
foo->bar = foo;
|
foo->bar = foo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{ // This tests destroying the data when storing the base class of a type
|
||||||
|
class ITest {
|
||||||
|
public:
|
||||||
|
size_t num = 0;
|
||||||
|
ITest() : num(1234) {};
|
||||||
|
};
|
||||||
|
|
||||||
|
class CA : public ITest {
|
||||||
|
public:
|
||||||
|
size_t num2 = 0;
|
||||||
|
CA() : ITest(), num2(4321) {};
|
||||||
|
};
|
||||||
|
|
||||||
|
class CB : public ITest {
|
||||||
|
public:
|
||||||
|
int num2 = 0;
|
||||||
|
CB() : ITest(), num2(-1) {};
|
||||||
|
};
|
||||||
|
|
||||||
|
ASP<ITest> genericAtomic = nullptr;
|
||||||
|
SP<ITest> genericNormal = nullptr;
|
||||||
|
{
|
||||||
|
auto derivedAtomic = makeAtomicShared<CA>();
|
||||||
|
auto derivedNormal = makeShared<CA>();
|
||||||
|
genericAtomic = derivedAtomic;
|
||||||
|
genericNormal = derivedNormal;
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPECT(!!genericAtomic, true);
|
||||||
|
EXPECT(!!genericNormal, true);
|
||||||
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -149,6 +181,5 @@ int main(int argc, char** argv, char** envp) {
|
||||||
EXPECT(*intPtr2, 10);
|
EXPECT(*intPtr2, 10);
|
||||||
|
|
||||||
EXPECT(testAtomicImpl(), 0);
|
EXPECT(testAtomicImpl(), 0);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue