lean cpp library
A lean C++ library providing efficient utility classes for high-performance C++ applications.
ref_counter.h
00001 /*****************************************************/
00002 /* lean Smart                   (c) Tobias Zirr 2011 */
00003 /*****************************************************/
00004 
00005 #ifndef LEAN_SMART_REF_COUNTER
00006 #define LEAN_SMART_REF_COUNTER
00007 
00008 #include <memory>
00009 #include "../lean.h"
00010 #include "../concurrent/atomic.h"
00011 
00012 namespace lean
00013 {
00014 namespace smart
00015 {
00016 
00018 template < class Counter = long, class Allocator = std::allocator<Counter> >
00019 class ref_counter
00020 {
00021 public:
00023     class ref_counts
00024     {
00025     private:
00026         // Allocator
00027         typedef typename Allocator::template rebind<ref_counts>::other allocator_type_;
00028         allocator_type_ m_allocator;
00029 
00030     protected:
00032         ref_counts(const allocator_type_& allocator, Counter references, Counter weakReferences)
00033             : m_allocator(allocator),
00034             references(references),
00035             weakReferences(weakReferences) { };
00036 #ifndef LEAN0X_NO_RVALUE_REFERENCES
00037 
00038         ref_counts(allocator_type_&& allocator, Counter references, Counter weakReferences)
00039             : m_allocator(::std::move(allocator)),
00040             references(references),
00041             weakReferences(weakReferences) { };
00042 #endif
00043 #ifndef LEAN_OPTIMIZE_DEFAULT_DESTRUCTOR
00044         // Destructor.
00045         ~ref_counts() throw() { };
00046 #endif
00047     public:
00049         typedef allocator_type_ allocator_type;
00050 
00052         Counter references;
00053 
00055         Counter weakReferences;
00056 
00058         static ref_counts* create(allocator_type allocator, Counter references, Counter weakReferences)
00059         {
00060             ref_counts *counts = allocator.allocate(1);
00061 
00062             try
00063             {
00064                 new (counts) ref_counts(allocator, references, weakReferences);
00065             }
00066             catch (...)
00067             {
00068                 allocator.deallocate(counts, 1);
00069                 throw;
00070             }
00071 
00072             return counts;
00073         }
00074 
00076         static void destroy(const ref_counts *counts)
00077         {
00078             LEAN_ASSERT(counts);
00079 
00080 #ifndef LEAN0X_NO_RVALUE_REFERENCES
00081             allocator_type allocator(::std::move(counts->m_allocator));
00082 #else
00083             allocator_type allocator(counts->m_allocator);
00084 #endif
00085             counts->~ref_counts();
00086             allocator.deallocate(const_cast<ref_counts*>(counts), 1);
00087         }
00088     };
00089 
00090     // Reference counts
00091     ref_counts *m_counts;
00092 
00094     static ref_counts* acquire(ref_counts *counts)
00095     {
00096         if (counts)
00097             atomic_increment(counts->weakReferences);
00098 
00099         return counts;
00100     }
00101 
00103     void release(ref_counts *counts)
00104     {
00105         // Clean up, if this is the last reference
00106         if (counts && !atomic_decrement(counts->weakReferences))
00107             ref_counts::destroy(counts);
00108     }
00109 
00111     explicit ref_counter(ref_counts *counts)
00112         : m_counts(counts) { }
00113 
00114 public:
00116     typedef Counter counter_type;
00118     typedef Allocator allocator_type;
00119 
00121     explicit ref_counter(counter_type references = 1)
00122         : m_counts( ref_counts::create(typename ref_counts::allocator_type(), references, 1) ) { }
00124     explicit ref_counter(allocator_type allocator)
00125         : m_counts( ref_counts::create(allocator, 1, 1) ) { }
00127     ref_counter(counter_type references, allocator_type allocator)
00128         : m_counts( ref_counts::create(allocator, references, 1) ) { }
00130     ref_counter(const ref_counter& refCounter)
00131         : m_counts( acquire(refCounter.m_counts) ) { }
00132 #ifndef LEAN0X_NO_RVALUE_REFERENCES
00133 
00134     ref_counter(ref_counter&& refCounter)
00135         : m_counts(std::move(refCounter.m_counts))
00136     {
00137         // Warning: this effectively "breaks" the other object
00138         refCounter.m_counts = nullptr;
00139     }
00140 #endif
00141 
00142     ~ref_counter() throw()
00143     {
00144         release(m_counts);
00145     }
00146 
00148     static LEAN_INLINE ref_counter null()
00149     {
00150         // Warning: this object is effectively "broken"
00151         return ref_counter(nullptr);
00152     }
00153 
00155     ref_counter& operator =(const ref_counter& refCounter)
00156     {
00157         // Don't re-assign the same
00158         if(m_counts != refCounter.m_counts)
00159         {
00160             ref_counts *prevReferences = m_counts;
00161             m_counts = acquire(refCounter.m_counts);
00162             release(prevReferences);
00163         }
00164 
00165         return *this;
00166     }
00167 #ifndef LEAN0X_NO_RVALUE_REFERENCES
00168 
00169     ref_counter& operator =(ref_counter&& refCounter)
00170     {
00171         // Don't re-assign the same
00172         if(m_counts != refCounter.m_counts)
00173         {
00174             ref_counts *prevReferences = m_counts;
00175 
00176             m_counts = ::std::move(refCounter.m_counts);
00177             // Warning: this effectively "breaks" the other object
00178             refCounter.m_counts = nullptr;
00179 
00180             release(prevReferences);
00181         }
00182 
00183         return *this;
00184     }
00185 #endif
00186 
00188     bool increment_checked() const
00189     {
00190         LEAN_ASSERT(m_counts);
00191 
00192         for (;;)
00193         {
00194             Counter references = (volatile Counter&)m_counts->references;
00195             
00196             // Make sure reference count has not become 0 yet
00197             if (references < 1)
00198                 return false;
00199             // Make sure value has not changed until it has successfully been updated
00200             else if (atomic_test_and_set(m_counts->references, references, references + 1))
00201                 return true;
00202         }
00203 
00204         LEAN_ASSERT(0);
00205     }
00207     counter_type increment() const
00208     {
00209         LEAN_ASSERT(m_counts);
00210 
00211         return atomic_increment(m_counts->references);
00212     }
00214     counter_type decrement() const
00215     {
00216         LEAN_ASSERT(m_counts);
00217 
00218         return atomic_decrement(m_counts->references);
00219     }
00220 
00222     LEAN_INLINE bool is_null() { return (m_counts == nullptr); }
00223 
00225     LEAN_INLINE counter_type count() const { LEAN_ASSERT(m_counts); return m_counts->references; };
00227     LEAN_INLINE bool valid() const { LEAN_ASSERT(m_counts); return (m_counts->references > 0); };
00228 
00230     LEAN_INLINE counter_type weak_count() const { LEAN_ASSERT(m_counts); return m_counts->weakReferences; };
00231 
00233     LEAN_INLINE ref_counter& operator ++() { ++const_cast<const ref_counter&>(*this); return *this; };
00235     LEAN_INLINE const ref_counter& operator ++() const { increment(); return *this; };
00237     LEAN_INLINE ref_counter& operator --() { --const_cast<const ref_counter&>(*this); return *this; };
00239     LEAN_INLINE const ref_counter& operator --() const { decrement(); return *this; };
00241     LEAN_INLINE counter_type operator ++(int) const { counter_type prevCount = count(); ++(*this); return prevCount; };
00243     LEAN_INLINE counter_type operator --(int) const { counter_type prevCount = count(); --(*this); return prevCount;};
00244 
00246     LEAN_INLINE operator counter_type() const { return count(); };
00248     LEAN_INLINE operator bool() const { return valid(); };
00249 };
00250 
00251 } // namespace
00252 
00253 using smart::ref_counter;
00254 
00255 } // namespace
00256 
00257 #endif