#ifndef ALIVE_PTR_H_INCLUDED
#define ALIVE_PTR_H_INCLUDED
#include <atomic>
namespace alive
{
template<typename T>
struct alive_ref
{
inline ~alive_ref() {}
inline T* data() { return ptr_.load(); }
bool ref(int* old_ref = nullptr)
{
bool exchanged = false;
auto old = ref_.load();
while (old > 0 && !(exchanged = ref_.compare_exchange_weak(old, old + 1)));
if (exchanged && old_ref)
{
*old_ref = old;
}
return exchanged;
}
bool deref(int* old_ref = nullptr)
{
bool exchanged = false;
auto old = ref_.load();
while (old > 0 && !(exchanged = ref_.compare_exchange_weak(old, old - 1)));
if (exchanged && old_ref)
{
*old_ref = old;
}
return exchanged;
}
std::atomic<T*> ptr_{ nullptr };
std::atomic<int> ref_{ 0 };
};
template<typename T>
class support_alive_ptr
{
typedef alive_ref<T> alive_ref_t;
public:
static alive_ref_t* getAndRef(T* ptr)
{
return internalGetAndRef(ptr);
}
inline ~support_alive_ptr()
{
auto ref_ptr = ref_.load();
if (ref_ptr)
{
ref_ptr->ptr_.store(nullptr);
int old_ref = 0;
if (ref_ptr->deref(&old_ref) && old_ref == 1)
{
ref_.store(nullptr);
delete ref_ptr;
}
}
}
inline int alive_ref_count() const
{
auto ref_ptr = ref_.load();
return ref_ptr ? ref_ptr->ref_.load() : 0;
}
private:
static alive_ref_t* internalGetAndRef(support_alive_ptr<T>* base)
{
alive_ref_t* ref_ptr = base->ref_.load();
if (ref_ptr)
{
ref_ptr->ref();
}
else
{
ref_ptr = new alive_ref_t;
ref_ptr->ptr_.store(static_cast<T*>(base));
ref_ptr->ref_.store(2); // 2: 1 held by support_alive_ptr self, 1 for the first alive_ptr
alive_ref_t* ref_ptr_held{ nullptr };
if (!base->ref_.compare_exchange_strong(ref_ptr_held, ref_ptr))
{
delete ref_ptr;
ref_ptr = ref_ptr_held;
ref_ptr->ref();
}
}
return ref_ptr;
}
static alive_ref_t* internalGetAndRef(...)
{
static_assert(false, "support_alive_ptr<T> is not base of T");
}
private:
std::atomic<alive_ref_t*> ref_{ nullptr };
};
template<typename T>
class alive_ptr
{
typedef alive_ref<T> alive_ref_t;
public:
inline alive_ptr() {}
inline alive_ptr(T* ptr) : ref_(ptr ? support_alive_ptr<T>::getAndRef(ptr) : nullptr) {}
inline alive_ptr(const alive_ptr& rhs) : ref_(rhs.ref_) { if (ref_) { ref_->ref(); } }
inline alive_ptr(alive_ptr&& rhs) : ref_(rhs.ref_) { if (ref_) { rhs.ref_ = nullptr; } }
inline ~alive_ptr() { tryToDerefAndDelete(); }
inline alive_ptr& operator=(const alive_ptr& rhs)
{
internalSet(rhs.ref_);
return *this;
}
inline alive_ptr& operator=(alive_ptr&& rhs)
{
if (rhs.ref_ != ref_)
{
tryToDerefAndDelete();
ref_ = rhs.ref_;
rhs.ref_ = nullptr;
}
return *this;
}
inline bool isNull() const { return ref_ == nullptr || ref_->data() == nullptr; }
inline bool isAlive() const { return !isNull(); }
inline bool operator !() const { return isNull(); }
inline T *data() const { return ref_ == nullptr ? nullptr : ref_->data(); }
inline T* operator->() const { return data(); }
inline T& operator*() const { return *data(); }
inline operator T*() const { return data(); }
inline void clear() { *this = alive_ptr(); }
private:
inline void internalSet(alive_ref_t* new_ref)
{
if (ref_ == new_ref) { return; }
if (new_ref) { new_ref->ref(); }
tryToDerefAndDelete();
ref_ = new_ref;
}
inline void tryToDerefAndDelete()
{
if (ref_)
{
int old_ref = 0;
if (ref_->deref(&old_ref) && old_ref == 1)
{
delete ref_;
}
}
}
private:
alive_ref_t* ref_{ nullptr };
};
}
#endif