/* ZReferenceCounter.hpp Author: James Russell Created: 3/22/2012 Purpose: Defines a structure that is usually allocated on the heap and will maintain a strong reference count, a weak reference count, and an integer flag used to signal state. This reference counter is designed to be thread safe by using atomic operations. License: TODO */ #pragma once #ifndef _ZREFERENCECOUNTER_HPP #define _ZREFERENCECOUNTER_HPP #include #include #include #include //The leading (negative) bit is used to indicate deallocation on the State int #define ZREFCOUNTER_DEALLOC_BIT (0x80000000) /* ZReferenceCounter::CombinedCount is a 32-bit integer value containing 2x16-bit integer values -- the strong and weak reference counts. They are stored as 0xWWWWSSSS, i.e. upper 16 bits are the weak reference count, and the lower 16 bits are the strong reference count. Do realize that this limits us to 65565 strong and 65565 weak references without error conditions. We find this to be an acceptable limit. */ //These define how much to shift by to affect the strong/weak counters #define ZREFCOUNTER_STRONG_SHIFT (0) #define ZREFCOUNTER_WEAK_SHIFT (16) //These masks are what we will use to mask the upper half (weak) and lower half (strong) parts //of the integer reference count #define ZREFCOUNTER_STRONG_MASK (0xFFFF << ZREFCOUNTER_STRONG_SHIFT) #define ZREFCOUNTER_WEAK_MASK (0xFFFF << ZREFCOUNTER_WEAK_SHIFT) //These values are used to add a single weak reference or a single strong reference to the //reference count #define ZREFCOUNTER_STRONG_REF (1 << ZREFCOUNTER_STRONG_SHIFT) #define ZREFCOUNTER_WEAK_REF (1 << ZREFCOUNTER_WEAK_SHIFT) //These macros extract the strong/weak reference counts respectively from the combined counts #define ZREFCOUNTER_EXTRACT_STRONG_REF(x) (((x) >> ZREFCOUNTER_STRONG_SHIFT) & 0xFFFF) #define ZREFCOUNTER_EXTRACT_WEAK_REF(x) (((x) >> ZREFCOUNTER_WEAK_SHIFT) & 0xFFFF) /* Reference counting struct. Keeps a reference count to an object (both strong and weak), and a state signaling integer. */ struct ZReferenceCounter { /* Integer we use for keeping track of strong and weak references within the space of a single native integer. The upper half is used to determine weak count. The lower half is used to determine strong count. This allows us to use atomic operations on both the strong reference count and weak reference count on architectures that do not have a DoubleCAS operation. Must be unsigned so >> doesn't do arthimetic shift. */ volatile uint32_t CombinedCount; /* Flag used for signaling object usage state > 0 - in use (number of users) 0 - not in use < 0 - ready for deallocation (discounting deallocation bit gives number of users) */ volatile int State; /* Default constructor. */ ZReferenceCounter() : CombinedCount(0), State(0) { } /* Destructor. */ ~ZReferenceCounter() { } /* Using atomic operations, will increment the strong reference count. */ inline void GainStrongRef() { SST_Atomic_Add((volatile int*)&CombinedCount, ZREFCOUNTER_STRONG_REF); } /* Using atomic operations, will increment the weak reference count. */ inline void GainWeakRef() { SST_Atomic_Add((volatile int*)&CombinedCount, ZREFCOUNTER_WEAK_REF); } /* Gets the current strong reference count. */ inline uint32_t GetStrongRefCount() { return ZREFCOUNTER_EXTRACT_STRONG_REF(CombinedCount); } /* Gets the current weak reference count. */ inline uint32_t GetWeakRefCount() { return ZREFCOUNTER_EXTRACT_WEAK_REF(CombinedCount); } /* Using atomic operations, will decrement the strong reference count and return the number of remaining references, both strong and weak, as part of the same value. To get the strong reference count, use ZREFCOUNTER_EXTRACT_STRONG_REF() on the return value. To get the weak reference count, use ZREFCOUNTER_EXTRACT_WEAK_REF() on the return value. */ inline uint32_t LoseStrongRef() { return SST_Atomic_AddReturn((volatile int*)&CombinedCount, -ZREFCOUNTER_STRONG_REF); } /* Using atomic operations, will decrement the weak reference count and return the number of remaining references, both strong and weak, as part of the same value. To get the strong reference count, use ZREFCOUNTER_EXTRACT_STRONG_REF() on the return value. To get the weak reference count, use ZREFCOUNTER_EXTRACT_WEAK_REF() on the return value. */ inline uint32_t LoseWeakRef() { return SST_Atomic_AddReturn((volatile int*)&CombinedCount, -ZREFCOUNTER_WEAK_REF); } /* Using atomic operations, will set the state flag as 'deallocated' as soon as possible, which will ensure anyone using 'SignalInUse' will get a 'false' return value. */ inline void SignalDeallocateObject() { //Check deallocated if (State & ZREFCOUNTER_DEALLOC_BIT) return; //Set high bit SST_Atomic_Or(&State, ZREFCOUNTER_DEALLOC_BIT); //Wait logic while ((State & ~ZREFCOUNTER_DEALLOC_BIT) != 0) continue; //State is guaranteed here to be == ZREFCOUNTER_DEALLOC_BIT } /* Using atomic operations, will set the state flag as 'in use'. If unable to set state flag as 'in use' because the object is deallocated, will return false. This is important to weak reference holders to signal to strong reference holders that the object should not be deallocated until the object is no longer in use. */ inline bool SignalInUse() { //Check deallocated if (State & ZREFCOUNTER_DEALLOC_BIT) return false; int val = SST_Atomic_IncReturn(&State); if (val & ZREFCOUNTER_DEALLOC_BIT) { SST_Atomic_Dec(&State); //Atomic decrement to ensure we don't starve the waiting deallocation thread return false; } else return true; } /* Using atomic operations, will set the state flag as 'no longer in use'. */ inline void SignalUnused() { SST_Atomic_Dec(&State); } }; /* Allocator class for ZReferenceCounter. Responsible for heap allocation and deletion of ZReferenceCounter instances. */ class ZReferenceCounterAllocator { public: /* public ZSmartPointerAllocator::AllocateCounter Allocation method that allocates a reference counter for use by the smart pointer. @return (ZSmartPointerRefCounter*) */ ZReferenceCounter* AllocateCounter() { return znew ZReferenceCounter(); } /* public ZSmartPointerAllocator::DeallocateCounter Deallocates a reference counter that was provided by AllocateCounter. @param _counter - the counter to deallocate */ void DeallocateCounter(ZReferenceCounter* _counter) { zdelete _counter; } }; #endif