summaryrefslogtreecommitdiff
path: root/src/safe_ptr.hh
diff options
context:
space:
mode:
authorMaxime Coste <frrrwww@gmail.com>2014-08-12 00:30:13 +0100
committerMaxime Coste <frrrwww@gmail.com>2014-08-12 19:18:10 +0100
commitd356ae241986282cf33a31874da1d3139960b63d (patch)
tree651bb910fa69fec45c9ffc09a47f6096ed492809 /src/safe_ptr.hh
parent1b54b65bb59b46718d7485e690835faf695b6410 (diff)
Make safe_ptr able to track callstacks
The code stays disabled, as the performance penalty is quite high, but can be enabled to help debugging safe pointers.
Diffstat (limited to 'src/safe_ptr.hh')
-rw-r--r--src/safe_ptr.hh167
1 files changed, 167 insertions, 0 deletions
diff --git a/src/safe_ptr.hh b/src/safe_ptr.hh
new file mode 100644
index 00000000..6a53a02e
--- /dev/null
+++ b/src/safe_ptr.hh
@@ -0,0 +1,167 @@
+#ifndef safe_ptr_hh_INCLUDED
+#define safe_ptr_hh_INCLUDED
+
+// #define SAFE_PTR_TRACK_CALLSTACKS
+
+#include "assert.hh"
+
+#ifdef SAFE_PTR_TRACK_CALLSTACKS
+
+#include <vector>
+#include <algorithm>
+#endif
+
+namespace Kakoune
+{
+
+// *** safe_ptr: objects that assert nobody references them when they die ***
+
+template<typename T>
+class safe_ptr
+{
+public:
+ safe_ptr() : m_ptr(nullptr) {}
+ explicit safe_ptr(T* ptr) : m_ptr(ptr)
+ {
+ #ifdef KAK_DEBUG
+ if (m_ptr)
+ m_ptr->inc_safe_count(this);
+ #endif
+ }
+ safe_ptr(const safe_ptr& other) : safe_ptr(other.m_ptr) {}
+ safe_ptr(safe_ptr&& other) : m_ptr(other.m_ptr)
+ {
+ other.m_ptr = nullptr;
+ #ifdef KAK_DEBUG
+ if (m_ptr)
+ m_ptr->safe_ptr_moved(&other, this);
+ #endif
+ }
+ ~safe_ptr()
+ {
+ #ifdef KAK_DEBUG
+ if (m_ptr)
+ m_ptr->dec_safe_count(this);
+ #endif
+ }
+
+ safe_ptr& operator=(const safe_ptr& other)
+ {
+ #ifdef KAK_DEBUG
+ if (m_ptr != other.m_ptr)
+ {
+ if (m_ptr)
+ m_ptr->dec_safe_count(this);
+ if (other.m_ptr)
+ other.m_ptr->inc_safe_count(this);
+ }
+ #endif
+ m_ptr = other.m_ptr;
+ return *this;
+ }
+
+ safe_ptr& operator=(safe_ptr&& other)
+ {
+ #ifdef KAK_DEBUG
+ if (m_ptr)
+ m_ptr->dec_safe_count(this);
+ if (other.m_ptr)
+ other.m_ptr->safe_ptr_moved(&other, this);
+ #endif
+ m_ptr = other.m_ptr;
+ other.m_ptr = nullptr;
+ return *this;
+ }
+
+ void reset(T* ptr = nullptr)
+ {
+ *this = safe_ptr(ptr);
+ }
+
+ bool operator== (const safe_ptr& other) const { return m_ptr == other.m_ptr; }
+ bool operator!= (const safe_ptr& other) const { return m_ptr != other.m_ptr; }
+ bool operator== (T* ptr) const { return m_ptr == ptr; }
+ bool operator!= (T* ptr) const { return m_ptr != ptr; }
+
+ T& operator* () const { return *m_ptr; }
+ T* operator-> () const { return m_ptr; }
+
+ T* get() const { return m_ptr; }
+
+ explicit operator bool() const { return m_ptr; }
+
+private:
+ T* m_ptr;
+};
+
+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
+ }
+
+ void inc_safe_count(void* ptr) const
+ {
+ ++m_count;
+ #ifdef SAFE_PTR_TRACK_CALLSTACKS
+ m_callstacks.emplace_back(ptr);
+ #endif
+ }
+ void dec_safe_count(void* ptr) const
+ {
+ --m_count;
+ kak_assert(m_count >= 0);
+ #ifdef SAFE_PTR_TRACK_CALLSTACKS
+ auto it = std::find_if(m_callstacks.begin(), m_callstacks.end(),
+ [=](const Callstack& cs) { return cs.ptr == ptr; });
+ kak_assert(it != m_callstacks.end());
+ m_callstacks.erase(it);
+ #endif
+ }
+
+ void safe_ptr_moved(void* from, void* to) const
+ {
+ #ifdef SAFE_PTR_TRACK_CALLSTACKS
+ auto it = std::find_if(m_callstacks.begin(), m_callstacks.end(),
+ [=](const Callstack& cs) { return cs.ptr == from; });
+ kak_assert(it != m_callstacks.end());
+ it->ptr = to;
+ #endif
+ }
+
+private:
+ #ifdef SAFE_PTR_TRACK_CALLSTACKS
+ struct Backtrace
+ {
+ static constexpr int max_frames = 16;
+ void* stackframes[max_frames];
+ int num_frames = 0;
+
+ Backtrace();
+ const char* desc() const;
+ };
+
+ struct Callstack
+ {
+ Callstack(void* p) : ptr(p) {}
+ void* ptr;
+ Backtrace bt;
+ };
+
+ mutable std::vector<Callstack> m_callstacks;
+ #endif
+ mutable int m_count;
+#endif
+};
+
+}
+
+#endif // safe_ptr_hh_INCLUDED
+