internal: new shared_ptr and weak_ptr implementation (#5883)
moves std::shared_ptrs to a new implementation Advantages: - you can dereference a weak_ptr directly. This will obviously segfault on a nullptr deref if it's expired. - this is useful to avoid the .lock() hell where we are 100% sure the pointer _should_ be valid. (and if it isn't, it should throw.) - weak_ptrs are still valid while the SP is being destroyed. - reasoning: while an object (e.g. CWindow) is being destroyed, its `weak_ptr self` should be accessible (the sp is still alive, and so is CWindow), but it's not because by stl it's already expired (to prevent resurrection) - this impl solves it differently. w_p is expired, but can still be dereferenced and used. Creating `s_p`s is not possible anymore, though. - this is useful in destructors and callbacks.
This commit is contained in:
parent
589f758d94
commit
1ed1ce9506
88 changed files with 899 additions and 414 deletions
|
|
@ -146,7 +146,7 @@ class CBaseAnimatedVariable {
|
|||
|
||||
protected:
|
||||
PHLWINDOWREF m_pWindow;
|
||||
std::weak_ptr<CWorkspace> m_pWorkspace;
|
||||
PHLWORKSPACEREF m_pWorkspace;
|
||||
PHLLSREF m_pLayer;
|
||||
|
||||
SAnimationPropertyConfig* m_pConfig = nullptr;
|
||||
|
|
|
|||
|
|
@ -123,7 +123,7 @@ void CMonitor::onConnect(bool noRule) {
|
|||
m_bRenderingInitPassed = true;
|
||||
}
|
||||
|
||||
std::shared_ptr<CMonitor>* thisWrapper = nullptr;
|
||||
SP<CMonitor>* thisWrapper = nullptr;
|
||||
|
||||
// find the wrap
|
||||
for (auto& m : g_pCompositor->m_vRealMonitors) {
|
||||
|
|
@ -337,7 +337,7 @@ void CMonitor::onDisconnect(bool destroy) {
|
|||
|
||||
g_pHyprRenderer->m_pMostHzMonitor = pMonitorMostHz;
|
||||
}
|
||||
std::erase_if(g_pCompositor->m_vMonitors, [&](std::shared_ptr<CMonitor>& el) { return el.get() == this; });
|
||||
std::erase_if(g_pCompositor->m_vMonitors, [&](SP<CMonitor>& el) { return el.get() == this; });
|
||||
}
|
||||
|
||||
void CMonitor::addDamage(const pixman_region32_t* rg) {
|
||||
|
|
@ -464,7 +464,7 @@ void CMonitor::setMirror(const std::string& mirrorOf) {
|
|||
|
||||
// push to mvmonitors
|
||||
|
||||
std::shared_ptr<CMonitor>* thisWrapper = nullptr;
|
||||
SP<CMonitor>* thisWrapper = nullptr;
|
||||
|
||||
// find the wrap
|
||||
for (auto& m : g_pCompositor->m_vRealMonitors) {
|
||||
|
|
@ -578,7 +578,7 @@ void CMonitor::changeWorkspace(const PHLWORKSPACE& pWorkspace, bool internal, bo
|
|||
}
|
||||
|
||||
if (!noFocus && !g_pCompositor->m_pLastMonitor->activeSpecialWorkspace &&
|
||||
!(g_pCompositor->m_pLastWindow.lock() && g_pCompositor->m_pLastWindow.lock()->m_bPinned && g_pCompositor->m_pLastWindow.lock()->m_iMonitorID == ID)) {
|
||||
!(g_pCompositor->m_pLastWindow.lock() && g_pCompositor->m_pLastWindow->m_bPinned && g_pCompositor->m_pLastWindow->m_iMonitorID == ID)) {
|
||||
static auto PFOLLOWMOUSE = CConfigValue<Hyprlang::INT>("input:follow_mouse");
|
||||
auto pWindow = pWorkspace->getLastFocusedWindow();
|
||||
|
||||
|
|
@ -636,7 +636,7 @@ void CMonitor::setSpecialWorkspace(const PHLWORKSPACE& pWorkspace) {
|
|||
|
||||
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(ID);
|
||||
|
||||
if (!(g_pCompositor->m_pLastWindow.lock() && g_pCompositor->m_pLastWindow.lock()->m_bPinned && g_pCompositor->m_pLastWindow.lock()->m_iMonitorID == ID)) {
|
||||
if (!(g_pCompositor->m_pLastWindow.lock() && g_pCompositor->m_pLastWindow->m_bPinned && g_pCompositor->m_pLastWindow->m_iMonitorID == ID)) {
|
||||
if (const auto PLAST = activeWorkspace->getLastFocusedWindow(); PLAST)
|
||||
g_pCompositor->focusWindow(PLAST);
|
||||
else
|
||||
|
|
@ -704,7 +704,7 @@ void CMonitor::setSpecialWorkspace(const PHLWORKSPACE& pWorkspace) {
|
|||
|
||||
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(ID);
|
||||
|
||||
if (!(g_pCompositor->m_pLastWindow.lock() && g_pCompositor->m_pLastWindow.lock()->m_bPinned && g_pCompositor->m_pLastWindow.lock()->m_iMonitorID == ID)) {
|
||||
if (!(g_pCompositor->m_pLastWindow.lock() && g_pCompositor->m_pLastWindow->m_bPinned && g_pCompositor->m_pLastWindow->m_iMonitorID == ID)) {
|
||||
if (const auto PLAST = pWorkspace->getLastFocusedWindow(); PLAST)
|
||||
g_pCompositor->focusWindow(PLAST);
|
||||
else
|
||||
|
|
|
|||
300
src/helpers/memory/SharedPtr.hpp
Normal file
300
src/helpers/memory/SharedPtr.hpp
Normal file
|
|
@ -0,0 +1,300 @@
|
|||
#pragma once
|
||||
|
||||
#include <typeinfo>
|
||||
#include <typeindex>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
||||
#define SP CSharedPointer
|
||||
|
||||
/*
|
||||
This is a custom impl of std::shared_ptr.
|
||||
It is not thread-safe like the STL one,
|
||||
but Hyprland is single-threaded anyways.
|
||||
|
||||
It differs a bit from how the STL one works,
|
||||
namely in the fact that it keeps the T* inside the
|
||||
control block, and that you can still make a CWeakPtr
|
||||
or deref an existing one inside the destructor.
|
||||
*/
|
||||
namespace CSharedPointer_ {
|
||||
|
||||
class impl_base {
|
||||
public:
|
||||
virtual ~impl_base(){};
|
||||
|
||||
virtual void inc() noexcept = 0;
|
||||
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;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class impl : public impl_base {
|
||||
public:
|
||||
impl(T* data) noexcept : _data(data) {
|
||||
;
|
||||
}
|
||||
|
||||
/* strong refcount */
|
||||
unsigned int _ref = 0;
|
||||
/* weak refcount */
|
||||
unsigned int _weak = 0;
|
||||
|
||||
T* _data = nullptr;
|
||||
|
||||
friend void swap(impl*& a, impl*& b) {
|
||||
impl* tmp = a;
|
||||
a = b;
|
||||
b = tmp;
|
||||
}
|
||||
|
||||
/* if the destructor was called,
|
||||
creating shared_ptrs is no longer valid */
|
||||
bool _destroying = false;
|
||||
|
||||
void _destroy() {
|
||||
if (!_data)
|
||||
return;
|
||||
|
||||
// first, we destroy the data, but keep the pointer.
|
||||
// this way, weak pointers will still be able to
|
||||
// reference and use, but no longer create shared ones.
|
||||
_destroying = true;
|
||||
__deleter(_data);
|
||||
// now, we can reset the data and call it a day.
|
||||
_data = nullptr;
|
||||
_destroying = false;
|
||||
}
|
||||
|
||||
std::default_delete<T> __deleter{};
|
||||
|
||||
//
|
||||
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 dataNonNull() noexcept {
|
||||
return _data;
|
||||
}
|
||||
|
||||
virtual ~impl() {
|
||||
destroy();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class CSharedPointer {
|
||||
public:
|
||||
template <typename X>
|
||||
using validHierarchy = typename std::enable_if<std::is_assignable<T*, X*>::value>;
|
||||
|
||||
/* creates a new shared pointer managing a resource
|
||||
avoid calling. Could duplicate ownership. Prefer makeShared */
|
||||
explicit CSharedPointer(T* object) noexcept {
|
||||
impl_ = new CSharedPointer_::impl<T>(object);
|
||||
increment();
|
||||
}
|
||||
|
||||
/* creates a shared pointer from a reference */
|
||||
template <typename U, typename = validHierarchy<U>>
|
||||
CSharedPointer(const CSharedPointer<U>& ref) noexcept {
|
||||
impl_ = ref.impl_;
|
||||
increment();
|
||||
}
|
||||
|
||||
CSharedPointer(const CSharedPointer& ref) noexcept {
|
||||
impl_ = ref.impl_;
|
||||
increment();
|
||||
}
|
||||
|
||||
template <typename U, typename = validHierarchy<U>>
|
||||
CSharedPointer(CSharedPointer<U>&& ref) noexcept {
|
||||
std::swap(impl_, ref.impl_);
|
||||
}
|
||||
|
||||
CSharedPointer(CSharedPointer&& ref) noexcept {
|
||||
std::swap(impl_, ref.impl_);
|
||||
}
|
||||
|
||||
/* allows weakPointer to create from an impl */
|
||||
CSharedPointer(CSharedPointer_::impl_base* implementation) noexcept {
|
||||
impl_ = implementation;
|
||||
increment();
|
||||
}
|
||||
|
||||
/* creates an empty shared pointer with no implementation */
|
||||
CSharedPointer() noexcept {
|
||||
; // empty
|
||||
}
|
||||
|
||||
/* creates an empty shared pointer with no implementation */
|
||||
CSharedPointer(std::nullptr_t) noexcept {
|
||||
; // empty
|
||||
}
|
||||
|
||||
~CSharedPointer() {
|
||||
// we do not decrement here,
|
||||
// because we want to preserve the pointer
|
||||
// in case this is the last owner.
|
||||
if (impl_ && impl_->ref() == 1)
|
||||
destroyImpl();
|
||||
else
|
||||
decrement();
|
||||
}
|
||||
|
||||
template <typename U, typename = validHierarchy<U>>
|
||||
CSharedPointer<T>& operator=(const CSharedPointer<U>& rhs) {
|
||||
if (impl_ == rhs.impl_)
|
||||
return *this;
|
||||
|
||||
decrement();
|
||||
impl_ = rhs.impl_;
|
||||
increment();
|
||||
return *this;
|
||||
}
|
||||
|
||||
CSharedPointer& operator=(const CSharedPointer& rhs) {
|
||||
if (impl_ == rhs.impl_)
|
||||
return *this;
|
||||
|
||||
decrement();
|
||||
impl_ = rhs.impl_;
|
||||
increment();
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename U, typename = validHierarchy<U>>
|
||||
CSharedPointer<T>& operator=(CSharedPointer<U>&& rhs) {
|
||||
std::swap(impl_, rhs.impl_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
CSharedPointer& operator=(CSharedPointer&& rhs) {
|
||||
std::swap(impl_, rhs.impl_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
operator bool() const {
|
||||
return impl_ && impl_->dataNonNull();
|
||||
}
|
||||
|
||||
bool operator==(const CSharedPointer& rhs) const {
|
||||
return impl_ == rhs.impl_;
|
||||
}
|
||||
|
||||
bool operator()(const CSharedPointer& lhs, const CSharedPointer& rhs) const {
|
||||
return (uintptr_t)lhs.impl_ < (uintptr_t)rhs.impl_;
|
||||
}
|
||||
|
||||
bool operator<(const CSharedPointer& rhs) const {
|
||||
return (uintptr_t)impl_ < (uintptr_t)rhs.impl_;
|
||||
}
|
||||
|
||||
T* operator->() const {
|
||||
return get();
|
||||
}
|
||||
|
||||
T& operator*() const {
|
||||
return *get();
|
||||
}
|
||||
|
||||
void reset() {
|
||||
decrement();
|
||||
impl_ = nullptr;
|
||||
}
|
||||
|
||||
T* get() const {
|
||||
return (T*)(impl_ ? static_cast<CSharedPointer_::impl<T>*>(impl_)->_data : nullptr);
|
||||
}
|
||||
|
||||
unsigned int strongRef() const {
|
||||
return impl_ ? impl_->ref() : 0;
|
||||
}
|
||||
|
||||
CSharedPointer_::impl_base* impl_ = nullptr;
|
||||
|
||||
private:
|
||||
/*
|
||||
no-op if there is no impl_
|
||||
may delete the stored object if ref == 0
|
||||
may delete and reset impl_ if ref == 0 and weak == 0
|
||||
*/
|
||||
void decrement() {
|
||||
if (!impl_)
|
||||
return;
|
||||
|
||||
impl_->dec();
|
||||
|
||||
// if ref == 0, we can destroy impl
|
||||
if (impl_->ref() == 0)
|
||||
destroyImpl();
|
||||
}
|
||||
/* no-op if there is no impl_ */
|
||||
void increment() {
|
||||
if (!impl_)
|
||||
return;
|
||||
|
||||
impl_->inc();
|
||||
}
|
||||
|
||||
/* destroy the pointed-to object
|
||||
if able, will also destroy impl */
|
||||
void destroyImpl() {
|
||||
// destroy the impl contents
|
||||
impl_->destroy();
|
||||
|
||||
// check for weak refs, if zero, we can also delete impl_
|
||||
if (impl_->wref() == 0) {
|
||||
delete impl_;
|
||||
impl_ = nullptr;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <typename U, typename... Args>
|
||||
static CSharedPointer<U> makeShared(Args&&... args) {
|
||||
return CSharedPointer<U>(new U(std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
struct std::hash<CSharedPointer<T>> {
|
||||
std::size_t operator()(const CSharedPointer<T>& p) const noexcept {
|
||||
return std::hash<void*>{}(p->impl_);
|
||||
}
|
||||
};
|
||||
188
src/helpers/memory/WeakPtr.hpp
Normal file
188
src/helpers/memory/WeakPtr.hpp
Normal file
|
|
@ -0,0 +1,188 @@
|
|||
#pragma once
|
||||
|
||||
#include "SharedPtr.hpp"
|
||||
|
||||
#define WP CWeakPointer
|
||||
|
||||
/*
|
||||
This is a Hyprland implementation of std::weak_ptr.
|
||||
|
||||
See SharedPtr.hpp for more info on how it's different.
|
||||
*/
|
||||
|
||||
template <typename T>
|
||||
class CWeakPointer {
|
||||
public:
|
||||
template <typename X>
|
||||
using validHierarchy = typename std::enable_if<std::is_assignable<T*, X*>::value>;
|
||||
|
||||
/* create a weak ptr from a reference */
|
||||
template <typename U, typename = validHierarchy<U>>
|
||||
CWeakPointer(const CSharedPointer<U>& ref) noexcept {
|
||||
if (!ref.impl_)
|
||||
return;
|
||||
|
||||
impl_ = ref.impl_;
|
||||
incrementWeak();
|
||||
}
|
||||
|
||||
/* create a weak ptr from another weak ptr */
|
||||
template <typename U, typename = validHierarchy<U>>
|
||||
CWeakPointer(const CWeakPointer<U>& ref) noexcept {
|
||||
if (!ref.impl_)
|
||||
return;
|
||||
|
||||
impl_ = ref.impl_;
|
||||
incrementWeak();
|
||||
}
|
||||
|
||||
CWeakPointer(const CWeakPointer& ref) noexcept {
|
||||
if (!ref.impl_)
|
||||
return;
|
||||
|
||||
impl_ = ref.impl_;
|
||||
incrementWeak();
|
||||
}
|
||||
|
||||
template <typename U, typename = validHierarchy<U>>
|
||||
CWeakPointer(CWeakPointer<U>&& ref) noexcept {
|
||||
std::swap(impl_, ref.impl_);
|
||||
}
|
||||
|
||||
CWeakPointer(CWeakPointer&& ref) noexcept {
|
||||
std::swap(impl_, ref.impl_);
|
||||
}
|
||||
|
||||
/* create a weak ptr from another weak ptr with assignment */
|
||||
template <typename U, typename = validHierarchy<U>>
|
||||
CWeakPointer<T>& operator=(const CWeakPointer<U>& rhs) {
|
||||
if (impl_ == rhs.impl_)
|
||||
return *this;
|
||||
|
||||
decrementWeak();
|
||||
impl_ = rhs.impl_;
|
||||
incrementWeak();
|
||||
return *this;
|
||||
}
|
||||
|
||||
CWeakPointer<T>& operator=(const CWeakPointer& rhs) {
|
||||
if (impl_ == rhs.impl_)
|
||||
return *this;
|
||||
|
||||
decrementWeak();
|
||||
impl_ = rhs.impl_;
|
||||
incrementWeak();
|
||||
return *this;
|
||||
}
|
||||
|
||||
/* create a weak ptr from a shared ptr with assignment */
|
||||
template <typename U, typename = validHierarchy<U>>
|
||||
CWeakPointer<T>& operator=(const CSharedPointer<U>& rhs) {
|
||||
if ((uintptr_t)impl_ == (uintptr_t)rhs.impl_)
|
||||
return *this;
|
||||
|
||||
decrementWeak();
|
||||
impl_ = rhs.impl_;
|
||||
incrementWeak();
|
||||
return *this;
|
||||
}
|
||||
|
||||
/* create an empty weak ptr */
|
||||
CWeakPointer() {
|
||||
;
|
||||
}
|
||||
|
||||
~CWeakPointer() {
|
||||
decrementWeak();
|
||||
}
|
||||
|
||||
/* expired MAY return true even if the pointer is still stored.
|
||||
the situation would be e.g. self-weak pointer in a destructor.
|
||||
for pointer validity, use valid() */
|
||||
bool expired() const {
|
||||
return !impl_ || !impl_->dataNonNull() || impl_->destroying();
|
||||
}
|
||||
|
||||
/* this means the pointed-to object is not yet deleted and can still be
|
||||
referenced, but it might be in the process of being deleted.
|
||||
check !expired() if you want to check whether it's valid and
|
||||
assignable to a SP. */
|
||||
bool valid() const {
|
||||
return impl_ && impl_->dataNonNull();
|
||||
}
|
||||
|
||||
void reset() {
|
||||
decrementWeak();
|
||||
impl_ = nullptr;
|
||||
}
|
||||
|
||||
CSharedPointer<T> lock() const {
|
||||
if (!impl_ || !impl_->dataNonNull() || impl_->destroying())
|
||||
return {};
|
||||
|
||||
return CSharedPointer<T>(impl_);
|
||||
}
|
||||
|
||||
/* this returns valid() */
|
||||
operator bool() const {
|
||||
return valid();
|
||||
}
|
||||
|
||||
bool operator==(const CWeakPointer<T>& rhs) const {
|
||||
return impl_ == rhs.impl_;
|
||||
}
|
||||
|
||||
bool operator==(const CSharedPointer<T>& rhs) const {
|
||||
return impl_ == rhs.impl_;
|
||||
}
|
||||
|
||||
bool operator()(const CWeakPointer& lhs, const CWeakPointer& rhs) const {
|
||||
return (uintptr_t)lhs.impl_ < (uintptr_t)rhs.impl_;
|
||||
}
|
||||
|
||||
bool operator<(const CWeakPointer& rhs) const {
|
||||
return (uintptr_t)impl_ < (uintptr_t)rhs.impl_;
|
||||
}
|
||||
|
||||
T* get() const {
|
||||
return (T*)(impl_ ? static_cast<CSharedPointer_::impl<T>*>(impl_)->_data : nullptr);
|
||||
}
|
||||
|
||||
T* operator->() const {
|
||||
return get();
|
||||
}
|
||||
|
||||
CSharedPointer_::impl_base* impl_ = nullptr;
|
||||
|
||||
private:
|
||||
/* no-op if there is no impl_ */
|
||||
void decrementWeak() {
|
||||
if (!impl_)
|
||||
return;
|
||||
|
||||
impl_->decWeak();
|
||||
|
||||
// we need to check for ->destroying,
|
||||
// because otherwise we could destroy here
|
||||
// and have a shared_ptr destroy the same thing
|
||||
// later (in situations where we have a weak_ptr to self)
|
||||
if (impl_->wref() == 0 && impl_->ref() == 0 && !impl_->destroying()) {
|
||||
delete impl_;
|
||||
impl_ = nullptr;
|
||||
}
|
||||
}
|
||||
/* no-op if there is no impl_ */
|
||||
void incrementWeak() {
|
||||
if (!impl_)
|
||||
return;
|
||||
|
||||
impl_->incWeak();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct std::hash<CWeakPointer<T>> {
|
||||
std::size_t operator()(const CWeakPointer<T>& p) const noexcept {
|
||||
return std::hash<void*>{}(p->impl_);
|
||||
}
|
||||
};
|
||||
|
|
@ -3,6 +3,7 @@
|
|||
#include <any>
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
#include "../../macros.hpp"
|
||||
|
||||
class CSignal;
|
||||
|
||||
|
|
@ -21,7 +22,7 @@ class CSignalListener {
|
|||
std::function<void(std::any)> m_fHandler;
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<CSignalListener> CHyprSignalListener;
|
||||
typedef SP<CSignalListener> CHyprSignalListener;
|
||||
|
||||
class CStaticSignalListener {
|
||||
public:
|
||||
|
|
|
|||
|
|
@ -20,8 +20,8 @@ void CSignal::emit(std::any data) {
|
|||
}
|
||||
|
||||
CHyprSignalListener CSignal::registerListener(std::function<void(std::any)> handler) {
|
||||
CHyprSignalListener listener = std::make_shared<CSignalListener>(handler);
|
||||
m_vListeners.emplace_back(std::weak_ptr<CSignalListener>(listener));
|
||||
CHyprSignalListener listener = makeShared<CSignalListener>(handler);
|
||||
m_vListeners.emplace_back(WP<CSignalListener>(listener));
|
||||
return listener;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,6 +19,6 @@ class CSignal {
|
|||
void registerStaticListener(std::function<void(void*, std::any)> handler, void* owner);
|
||||
|
||||
private:
|
||||
std::vector<std::weak_ptr<CSignalListener>> m_vListeners;
|
||||
std::vector<WP<CSignalListener>> m_vListeners;
|
||||
std::vector<std::unique_ptr<CStaticSignalListener>> m_vStaticListeners;
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue