From bc35e185e7b261a2302ecfcba9cf8942d87513b6 Mon Sep 17 00:00:00 2001 From: King_DuckZ Date: Thu, 18 Feb 2016 19:07:37 +0100 Subject: [PATCH] Import safe_ptr from Kakoune project --- include/kakoune/ref_ptr.hh | 125 ++++++++++++++++++++++++++++++++++++ include/kakoune/safe_ptr.hh | 95 +++++++++++++++++++++++++++ 2 files changed, 220 insertions(+) create mode 100644 include/kakoune/ref_ptr.hh create mode 100644 include/kakoune/safe_ptr.hh diff --git a/include/kakoune/ref_ptr.hh b/include/kakoune/ref_ptr.hh new file mode 100644 index 0000000..a061cff --- /dev/null +++ b/include/kakoune/ref_ptr.hh @@ -0,0 +1,125 @@ +#ifndef ref_ptr_hh_INCLUDED +#define ref_ptr_hh_INCLUDED + +#include + +namespace Kakoune +{ + +struct WorstMatch { [[gnu::always_inline]] WorstMatch(...) {} }; + +[[gnu::always_inline]] +inline void ref_ptr_moved(WorstMatch, void*, void*) noexcept {} + +template +struct RefPtr +{ + RefPtr() = default; + explicit RefPtr(T* ptr) : m_ptr(ptr) { acquire(); } + ~RefPtr() { release(); } + RefPtr(const RefPtr& other) : m_ptr(other.m_ptr) { acquire(); } + RefPtr(RefPtr&& other) + noexcept(noexcept(std::declval().moved(nullptr))) + : m_ptr(other.m_ptr) { other.m_ptr = nullptr; moved(&other); } + + RefPtr& operator=(const RefPtr& other) + { + if (other.m_ptr != m_ptr) + { + release(); + m_ptr = other.m_ptr; + acquire(); + } + return *this; + } + + RefPtr& operator=(RefPtr&& other) + { + release(); + m_ptr = other.m_ptr; + other.m_ptr = nullptr; + moved(&other); + return *this; + } + + RefPtr& operator=(T* ptr) + { + if (ptr != m_ptr) + { + release(); + m_ptr = ptr; + acquire(); + } + return *this; + } + + [[gnu::always_inline]] + T* operator->() const { return m_ptr; } + [[gnu::always_inline]] + T& operator*() const { return *m_ptr; } + + [[gnu::always_inline]] + T* get() const { return m_ptr; } + + [[gnu::always_inline]] + explicit operator bool() const { return m_ptr; } + + void reset(T* ptr = nullptr) + { + if (ptr == m_ptr) + return; + release(); + m_ptr = ptr; + acquire(); + } + + friend bool operator==(const RefPtr& lhs, const RefPtr& rhs) { return lhs.m_ptr == rhs.m_ptr; } + friend bool operator!=(const RefPtr& lhs, const RefPtr& rhs) { return lhs.m_ptr != rhs.m_ptr; } + +private: + T* m_ptr = nullptr; + + [[gnu::always_inline]] + void acquire() + { + if (m_ptr) + inc_ref_count(static_cast(m_ptr), this); + } + + [[gnu::always_inline]] + void release() + { + if (m_ptr) + dec_ref_count(static_cast(m_ptr), this); + m_ptr = nullptr; + } + + [[gnu::always_inline]] + void moved(void* from) + noexcept(noexcept(ref_ptr_moved(static_cast(nullptr), nullptr, nullptr))) + { + if (m_ptr) + ref_ptr_moved(static_cast(m_ptr), from, this); + } +}; + +struct RefCountable +{ + int refcount = 0; + virtual ~RefCountable() = default; + + friend void inc_ref_count(RefCountable* r, void*) + { + ++r->refcount; + } + + friend void dec_ref_count(RefCountable* r, void*) + { + if (--r->refcount == 0) + delete r; + } +}; + +} + +#endif // ref_ptr_hh_INCLUDED diff --git a/include/kakoune/safe_ptr.hh b/include/kakoune/safe_ptr.hh new file mode 100644 index 0000000..12a9b4a --- /dev/null +++ b/include/kakoune/safe_ptr.hh @@ -0,0 +1,95 @@ +#ifndef safe_ptr_hh_INCLUDED +#define safe_ptr_hh_INCLUDED + +// #define SAFE_PTR_TRACK_CALLSTACKS + +#include "assert.hh" +#include "ref_ptr.hh" +#include "backtrace.hh" + +#include +#include + +#ifdef SAFE_PTR_TRACK_CALLSTACKS +#include "vector.hh" +#include +#endif + +namespace Kakoune +{ + +// *** SafePtr: objects that assert nobody references them when they die *** + +class SafeCountable +{ +public: +#ifdef KAK_DEBUG + SafeCountable() : m_count(0) {} + ~SafeCountable() + { + kak_assert(m_count == 0); + #ifdef SAFE_PTR_TRACK_CALLSTACKS + kak_assert(m_callstacks.empty()); + #endif + } + + friend void inc_ref_count(const SafeCountable* sc, void* ptr) + { + ++sc->m_count; + #ifdef SAFE_PTR_TRACK_CALLSTACKS + sc->m_callstacks.emplace_back(ptr); + #endif + } + + friend void dec_ref_count(const SafeCountable* sc, void* ptr) + { + --sc->m_count; + kak_assert(sc->m_count >= 0); + #ifdef SAFE_PTR_TRACK_CALLSTACKS + auto it = std::find_if(sc->m_callstacks.begin(), sc->m_callstacks.end(), + [=](const Callstack& cs) { return cs.ptr == ptr; }); + kak_assert(it != sc->m_callstacks.end()); + sc->m_callstacks.erase(it); + #endif + } + + friend void ref_ptr_moved(const SafeCountable* sc, void* from, void* to) + { + #ifdef SAFE_PTR_TRACK_CALLSTACKS + auto it = std::find_if(sc->m_callstacks.begin(), sc->m_callstacks.end(), + [=](const Callstack& cs) { return cs.ptr == from; }); + kak_assert(it != sc->m_callstacks.end()); + it->ptr = to; + #endif + } + +private: + #ifdef SAFE_PTR_TRACK_CALLSTACKS + struct Callstack + { + Callstack(void* p) : ptr(p) {} + void* ptr; + Backtrace bt; + }; + + mutable Vector m_callstacks; + #endif + mutable int m_count; +#else + [[gnu::always_inline]] + friend void inc_ref_count(const SafeCountable* sc, void* ptr) {} + + [[gnu::always_inline]] + friend void dec_ref_count(const SafeCountable* sc, void* ptr) {} +#endif +}; + +template +using PropagateConst = typename std::conditional::value, const U, U>::type; + +template +using SafePtr = RefPtr>; + +} + +#endif // safe_ptr_hh_INCLUDED