#pragma once #include "rbx/debug.h" #include "rbx/atomic.h" #include "rbx/declarations.h" #include "boost/cast.hpp" /// Forward Declarations namespace rbx { /* quick_intrusive_ptr_target<> is a mix-in that implements all functions required to use boost::intrusive_ptr. maxRefs should be used if Count is a byte or short and there is the risk of an overflow. Some algorithms do not have this risk. To avoid writing less performant code, maxRefs is not strictly enforced. In a multithreaded environment you should pick a "reasonable" value that is less than std::numeric_limits::max. For example, if Count=byte, then maxRefs could be 240 */ template class quick_intrusive_ptr_target; /* intrusive_ptr_target<> is a mix-in that implements all functions required to use boost::intrusive_ptr and rbx::intrusive_weak_ptr. If you don't need weak reference support, then use quick_intrusive_ptr_target TODO: Allow custom allocators */ template class intrusive_ptr_target; } /// Template Specialization for boost intrusive ptrs namespace boost { /// Template Specialization for boost intrusive ptrs for quick_intrusive_ptr_target template void intrusive_ptr_add_ref(const rbx::quick_intrusive_ptr_target * p); template void intrusive_ptr_release(const rbx::quick_intrusive_ptr_target * p); /// Template Specialization for boost intrusive ptrs for intrusive_ptr_target template void intrusive_ptr_add_ref(const rbx::intrusive_ptr_target * p); template void intrusive_ptr_release(const rbx::intrusive_ptr_target * p); template bool intrusive_ptr_expired(const rbx::intrusive_ptr_target * p); template bool intrusive_ptr_try_lock(const rbx::intrusive_ptr_target * p); template void intrusive_ptr_add_weak_ref(const rbx::intrusive_ptr_target * p); template void intrusive_ptr_weak_release(const rbx::intrusive_ptr_target * p); } namespace rbx { class too_many_refs : public std::exception { public: virtual const char* what() const throw() { return "too many refs"; } }; #pragma pack(push) #pragma pack(8) // Packing is useful if Count is short or byte template class RBXBaseClass quick_intrusive_ptr_target { private: rbx::atomic refs; public: inline quick_intrusive_ptr_target() { refs = 0; } friend void boost::intrusive_ptr_add_ref<>(const quick_intrusive_ptr_target* p); friend void boost::intrusive_ptr_release<>(const quick_intrusive_ptr_target* p); }; #pragma pack(pop) template class RBXBaseClass intrusive_ptr_target { private: // The "counts" struct is placed in memory at the head of the object #pragma pack(push) #pragma pack(8) // Packing is useful if Count is short or byte struct counts { rbx::atomic strong; // #shared rbx::atomic weak; // #weak + (#shared != 0) counts() { strong = 0; weak = 1; } }; #pragma pack(pop) static inline counts* fetch(const T* t) { return reinterpret_cast((char*) t - sizeof(counts)); } public: void* operator new(std::size_t t) { void* c = ::malloc(sizeof(counts) + t); // placement new the counts: new (c) counts(); return (char*)c + sizeof(counts); } void operator delete( void * p ) { counts* c = fetch(reinterpret_cast(p)); // operator delete should only be called if this object // never got touched by the intrusive_ptr functions RBXASSERT(c->strong == 0); RBXASSERT(c->weak == 1); ::free(c); } friend void boost::intrusive_ptr_add_ref<>(const intrusive_ptr_target* p); friend void boost::intrusive_ptr_release<>(const intrusive_ptr_target* p); friend void boost::intrusive_ptr_add_weak_ref<>(const intrusive_ptr_target* p); friend bool boost::intrusive_ptr_expired<>(const intrusive_ptr_target* p); friend bool boost::intrusive_ptr_try_lock<>(const intrusive_ptr_target* p); friend void boost::intrusive_ptr_weak_release<>(const intrusive_ptr_target* p); }; } namespace boost { /// Template specialization quick_intrusive_ptr_target template void intrusive_ptr_add_ref(const rbx::quick_intrusive_ptr_target* p) { if (maxRefs > 0 && p->refs >= maxRefs) throw rbx::too_many_refs(); const_cast*>(p)->refs++; } template void intrusive_ptr_release(const rbx::quick_intrusive_ptr_target* p) { RBXASSERT(p->refs > 0); if (--(const_cast*>(p)->refs) == 0) delete static_cast(p); } /// Template specialization intrusive_ptr_target template void intrusive_ptr_add_ref(const rbx::intrusive_ptr_target* p) { typename rbx::intrusive_ptr_target::counts* c = rbx::intrusive_ptr_target::fetch(static_cast(p)); if (maxStrong > 0) { if (++(c->strong) > maxStrong) throw rbx::too_many_refs(); } else { c->strong++; RBXASSERT(c->strong < std::numeric_limits::max() - 10); } } template void intrusive_ptr_release(const rbx::intrusive_ptr_target* p) { const T* t = static_cast(p); typedef typename rbx::intrusive_ptr_target::counts Counts; Counts* c = rbx::intrusive_ptr_target::fetch(t); if (--(c->strong) == 0) { // placement delete the object, but not the counts t->~T(); if (--(c->weak) == 0) { // placement delete the counts and reclaim composite object memory c->Counts::~counts(); ::free((void*)c); } } } template void intrusive_ptr_add_weak_ref(const rbx::intrusive_ptr_target * p) { typename rbx::intrusive_ptr_target::counts* c = rbx::intrusive_ptr_target::fetch(static_cast(p)); RBXASSERT(c->strong > 0); if (maxWeak > 0) { if (++(c->weak) > maxWeak + 1) // weak already has a ref because of the strong refs throw rbx::too_many_refs(); } else { ++(c->weak); RBXASSERT(c->weak < std::numeric_limits::max() - 10); } } template bool intrusive_ptr_expired(const rbx::intrusive_ptr_target * p) { typename rbx::intrusive_ptr_target::counts* c = rbx::intrusive_ptr_target::fetch(static_cast(p)); return c->strong == 0; } template bool intrusive_ptr_try_lock(const rbx::intrusive_ptr_target * p) { typename rbx::intrusive_ptr_target::counts* c = rbx::intrusive_ptr_target::fetch(static_cast(p)); while (true) { Count tmp = c->strong; if( tmp == 0 ) return false; if (c->strong.compare_and_swap(tmp + 1, tmp) == tmp) return true; } } template void intrusive_ptr_weak_release(const rbx::intrusive_ptr_target * p) { const T* t = static_cast(p); typedef typename rbx::intrusive_ptr_target::counts Counts; Counts* c = rbx::intrusive_ptr_target::fetch(t); if (--(c->weak) == 0) { RBXASSERT(c->strong == 0); // placement delete the counts and reclaim composite object memory c->Counts::~counts(); ::free((void*)c); } } }