Dryad/DryadVertex/VertexHost/system/classlib/include/DrRefCounter.h

840 lines
22 KiB
C++

/*
Copyright (c) Microsoft Corporation
All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in
compliance with the License. You may obtain a copy of the License
at http://www.apache.org/licenses/LICENSE-2.0
THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER
EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF
TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT.
See the Apache Version 2.0 License for specific language governing permissions and
limitations under the License.
*/
#ifndef __DRYADREFCOUNTER_H__
#define __DRYADREFCOUNTER_H__
#pragma once
template<class Ty> __forceinline Ty& DrRemoveConst(const Ty& x)
{
return const_cast<Ty&>(x);
}
template<class Ty> __forceinline Ty *DrRemovePtrConst(const Ty *pX)
{
return const_cast<Ty *>(pX);
}
UInt64 GetUniqueObjectID();
// a type for automatically assigning a unique object ID for static initializers
class DrObjID
{
public:
DrObjID() { _val = GetUniqueObjectID();}
UInt64 Value() const { return _val; };
private:
UInt64 _val;
};
/*
* DrRefCounter
*
* Implements simple reference count functionality.
* Derive any ref counted objects from this class.
*/
class DrRefCounter;
static const LONG k_lDecommissionedRefCount = (LONG)-1234;
class DrRefCountMonitor
{
public:
virtual void OnRefCountChanged(const DrRefCounter *pCounter, void *pContext, LONG newCount, const char *pszReason) = 0;
};
class IDrRefCounter
{
public:
virtual LONG IncRef() = 0;
LONG IncRef() const
{
// IncRef/DecRef are allowed even for const instances
return DrRemovePtrConst(this)->IncRef();
}
// called by weak reference pointers (under the shared lock) to IncRef this object.
// returns TRUE if the IncRef could be performed, false if the object is already unreferenceable
// The default implementation succeeds unless the current refcount is 0, on the assumption that the implementation
// will clear all existing weak references after the refcount becomes 0 and before the object is deleted.
virtual bool IncRefFromWeakReferenceLocked() = 0;
bool IncRefFromWeakReferenceLocked() const
{
// IncRef/DecRef are allowed even for const instances
return DrRemovePtrConst(this)->IncRefFromWeakReferenceLocked();
}
// called by weak reference Freememory implementations (under the shared lock) to get the refcount on this object.
virtual LONG GetRefCountLocked() const = 0;
virtual LONG DecRef() = 0;
LONG DecRef() const
{
// IncRef/DecRef are allowed even for const instances
return DrRemovePtrConst(this)->DecRef();
}
virtual void FreeMemory() = 0;
void FreeMemory() const
{
// FreeMemory is allowed even for const instances
DrRemovePtrConst(this)->FreeMemory();
}
virtual ~IDrRefCounter()
{
}
virtual UInt64 GetOID() const = 0; // Get object-id, an (almost) unique identifier that can be used for logging (this way we can see related log lines for a given object)
};
class DrOneInitializedVolatileLong
{
public:
volatile LONG m_long;
DrOneInitializedVolatileLong()
{
m_long = 1;
}
};
class DrOneInitializedZeroDestroyedVolatileLong : public DrOneInitializedVolatileLong
{
public:
~DrOneInitializedZeroDestroyedVolatileLong()
{
LogAssert(m_long == 0 || m_long == k_lDecommissionedRefCount);
}
};
class DrOneInitializedOneDestroyedVolatileLong : public DrOneInitializedVolatileLong
{
public:
~DrOneInitializedOneDestroyedVolatileLong()
{
LogAssert(m_long == 1 || m_long == k_lDecommissionedRefCount);
}
};
#define DRREFCOUNTNOIMPL \
public: \
virtual LONG IncRef() override = 0; \
virtual LONG DecRef() override = 0; \
virtual UInt64 GetOID() const override = 0; \
virtual bool IncRefFromWeakReferenceLocked() override = 0; \
virtual LONG GetRefCountLocked() const override = 0; \
virtual void FreeMemory() override = 0; \
// NOTE: the following could use DrOneInitializedZeroDestroyedVolativeLong, but some people create static instances of this...
#define DRREFCOUNTIMPL_NOFREEMEMORY_BASE(InterfaceClass) \
protected: \
mutable DrOneInitializedVolatileLong m_iRefCount; \
DrObjID m_oid; \
public: \
virtual LONG IncRef() override\
{ \
LONG i; \
i = InterlockedIncrement (&(m_iRefCount.m_long)); \
LogAssert (i > 1); \
return i; \
} \
virtual LONG DecRef() override\
{ \
LONG i; \
i = InterlockedDecrement (&(m_iRefCount.m_long)); \
if (i <= 0) \
{ \
LogAssert (i == 0); \
FreeMemory (); \
} \
return i; \
} \
virtual UInt64 GetOID() const override{ return m_oid.Value(); } \
virtual bool IncRefFromWeakReferenceLocked() override\
{ \
if (m_iRefCount.m_long == 0) { \
return false; \
} \
LONG i = InterlockedIncrement (&(m_iRefCount.m_long)); \
LogAssert (i > 1); \
return true; \
} \
virtual LONG GetRefCountLocked() const override\
{ \
return m_iRefCount.m_long; \
} \
#define DRREFCOUNTIMPL_STATIC_BASE(InterfaceClass) \
private: \
static void * operator new( size_t) { LogAssert(false); return NULL; } \
static void operator delete( void *) { LogAssert(false); } \
protected: \
mutable DrOneInitializedOneDestroyedVolatileLong m_iRefCount; \
DrObjID m_oid; \
public: \
virtual LONG IncRef() override\
{ \
LONG i; \
i = InterlockedIncrement (&(m_iRefCount.m_long)); \
LogAssert (i > 1); \
return i; \
} \
virtual LONG DecRef() override\
{ \
LONG i; \
i = InterlockedDecrement (&(m_iRefCount.m_long)); \
LogAssert(i > 0); \
return i; \
} \
virtual UInt64 GetOID() const override{ return m_oid.Value(); } \
virtual bool IncRefFromWeakReferenceLocked() override \
{ \
LONG i = InterlockedIncrement (&(m_iRefCount.m_long)); \
LogAssert (i > 1); \
return true; \
} \
virtual LONG GetRefCountLocked() const override\
{ \
return m_iRefCount.m_long; \
} \
#define DRREFCOUNTIMPL_NOFREEMEMORY DRREFCOUNTIMPL_NOFREEMEMORY_BASE(IDrRefCounter)
#define DRREFCOUNTIMPL_BASE(InterfaceClass) \
DRREFCOUNTIMPL_NOFREEMEMORY_BASE(InterfaceClass) \
protected: \
virtual void FreeMemory() override\
{ \
delete this; \
}
#define DRREFCOUNTIMPL DRREFCOUNTIMPL_BASE(IDrRefCounter)
#define DRREFCOUNTIMPL_STATIC DRREFCOUNTIMPL_STATIC_BASE(IDrRefCounter)
class DrRefCounter : public IDrRefCounter
{
protected:
mutable volatile LONG m_iRefCount;
DrRefCountMonitor *m_pMonitor;
void *m_pMonitorContext;
UInt64 m_oid;
DrRefCounter()
{
m_iRefCount = 1;
m_pMonitor = NULL;
m_pMonitorContext = NULL;
m_oid = GetUniqueObjectID();
}
virtual ~DrRefCounter()
{
LogAssert (m_iRefCount == 0 || m_iRefCount == k_lDecommissionedRefCount);
}
// Called when the refcount becomes zero.
virtual void FreeMemory() override
{
delete this;
}
public:
// not threadsafe
void SetRefCountMonitor(DrRefCountMonitor *pMonitor, void *pContext = NULL)
{
m_pMonitor = pMonitor;
m_pMonitorContext = pContext;
if (m_pMonitor != NULL) {
m_pMonitor->OnRefCountChanged(this, m_pMonitorContext, m_iRefCount, "SetMonitor");
}
}
virtual LONG IncRef() override
{
LONG i;
i = InterlockedIncrement (&m_iRefCount);
LogAssert (i > 1);
if (m_pMonitor != NULL) {
m_pMonitor->OnRefCountChanged(this, m_pMonitorContext, i, "IncRef");
}
return i;
}
// Increments without checking that the previous refcount was > 0. Used for special circumstances where we
// may temporarily increment from a 0 refcount
LONG IncRefNoCheck()
{
LONG i;
i = InterlockedIncrement (&m_iRefCount);
if (m_pMonitor != NULL) {
m_pMonitor->OnRefCountChanged(this, m_pMonitorContext, i, "IncRefNoCheck");
}
return i;
}
// called by weak reference pointers (under the shared lock) to IncRef this object.
// returns TRUE if the IncRef could be performed, false if the object is already unreferenceable
// The default implementation succeeds unless the current refcount is 0, on the assumption that the implementation
// will clear all existing weak references after the refcount becomes 0 and before the object is deleted.
virtual bool IncRefFromWeakReferenceLocked() override
{
if (m_iRefCount == 0) {
return false;
}
LONG i = InterlockedIncrement (&m_iRefCount);
LogAssert (i > 1); \
if (m_pMonitor != NULL) {
m_pMonitor->OnRefCountChanged(this, m_pMonitorContext, i, "IncRefFromWeakReferenceLocked");
}
return true;
}
virtual LONG GetRefCountLocked() const override
{
return m_iRefCount;
}
virtual LONG DecRef() override
{
LONG i;
if (m_pMonitor != NULL) {
// HACK: the refcount may be wrong, but it will work
m_pMonitor->OnRefCountChanged(this, m_pMonitorContext, m_iRefCount-1L, "DecRef");
}
i = InterlockedDecrement (&m_iRefCount);
if (i <= 0)
{
LogAssert (i == 0);
FreeMemory ();
}
return i;
}
// Decrements without special handling when the refcount becomes zero. Used for special circumstances
// where the refcount has been temporarily incremented from zero and another context will be
// calling FreeMemory().
LONG DecRefNoFree()
{
LONG i;
if (m_pMonitor != NULL) {
// HACK: the refcount may be wrong, but it will work
m_pMonitor->OnRefCountChanged(this, m_pMonitorContext, m_iRefCount-1L, "DecRefNoFree");
}
i = InterlockedDecrement (&m_iRefCount);
LogAssert(i >= 0);
return i;
}
virtual UInt64 GetOID() const override
{
return m_oid;
}
void ResetRefCounter()
{
LONG i;
i = InterlockedExchange (&m_iRefCount, 1);
if ( i != 0 )
{
DrLogE( "DecRef error - refCount=%ld, oid=%I64x", i, GetOID() );
LogAssert (i == 0);
}
if (m_pMonitor != NULL) {
m_pMonitor->OnRefCountChanged(this, m_pMonitorContext, 1, "ResetRefCounter");
}
}
// Abandons the refcounter. Used when a subclass overrides the implementation. Prevents the refcounter from being used, and
// prevents an assertion failure when the object is destructed.
void DecommissionRefCounter()
{
LONG i;
// We set a magic number to indicate that it is decommissioned. Any call to IncRef or DecRef will assert, and destruction will succeed
i = InterlockedExchange (&m_iRefCount, k_lDecommissionedRefCount);
LogAssert(i == 1 || i == k_lDecommissionedRefCount);
}
// The value returned by this method is not stable unless the caller provides another mechanism for guaranteeing that
// noone calls IncRef or DecRef.
LONG GetRefCount() const
{
return m_iRefCount;
}
};
template <class T> class DrRef
{
public:
DrRef()
{
p = NULL;
}
DrRef(T* lp)
{
p = lp;
if (p != NULL) {
DrRemovePtrConst(p)->IncRef();
}
}
explicit DrRef(const DrRef<T>& ref)
{
p = ref.p;
if (p != NULL) {
DrRemovePtrConst(p)->IncRef();
}
}
~DrRef()
{
if (p != NULL) {
DrRemovePtrConst(p)->DecRef();
p = NULL; // Make sure we AV in case someone is using DrRef after DecRef
}
}
operator T*() const
{
return p;
}
T& operator*() const
{
return *p;
}
T* operator->() const
{
return p;
}
bool operator!() const
{
return (p == NULL);
}
bool operator<(T* pT) const
{
return (p < pT);
}
bool operator>(T* pT) const
{
return (p < pT);
}
bool operator<=(T* pT) const
{
return (p <= pT);
}
bool operator>=(T* pT) const
{
return (p < pT);
}
bool operator==(T* pT) const
{
return (p == pT);
}
bool operator!=(T* pT) const
{
return (p != pT);
}
DrRef& Set(T* lp)
{
if (p != lp) {
if (lp != NULL) {
DrRemovePtrConst(lp)->IncRef();
}
if (p != NULL) {
DrRemovePtrConst(p)->DecRef();
}
p = lp;
}
return *this;
}
DrRef& operator=(T* lp)
{
return Set(lp);
}
DrRef& operator=(const DrRef<T>& ref)
{
return Set(ref.p);
}
// Release the interface and set to NULL
void Release()
{
T* pTemp = p;
if (pTemp != NULL) {
p = NULL;
DrRemovePtrConst(pTemp)->DecRef();
}
}
//
// Attach to an existing interface (does not IncRef)
//
void Attach(T* p2)
{
//
// Remove reference to previous interface
//
if (p != NULL)
{
DrRemovePtrConst(p)->DecRef();
}
//
// Update current interface
//
p = p2;
}
// Detach the interface (does not DecRef)
T* Detach()
{
T* pt = p;
p = NULL;
return pt;
}
void TransferFrom( DrRef<T>& source)
{
Attach(source.Detach());
}
template<class T2> void TransferFrom( DrRef<T2>& source)
{
T2 *p2 = source.Detach();
if (p != NULL) {
DrRemovePtrConst(p)->DecRef();
}
if (p2== NULL) {
p = NULL;
} else {
p = dynamic_cast<T *>(p2);
LogAssert(p != NULL);
}
}
T* Ptr() const
{
return p;
}
private:
T* p;
};
// Growable vector of DrRef smart pointers to arbitrary refcounted typed items
// Insertions and deletions can be performed both at the head and at the tail of the list, making it suitable for queues.
template<class t> class DrRefList
{
public:
DrRefList()
{
m_nEntries = 0;
m_nAllocated = 0;
m_prgEntries = NULL;
}
~DrRefList()
{
if (m_prgEntries != NULL) {
delete[] m_prgEntries;
m_prgEntries = NULL;
}
m_nEntries = 0;
m_nAllocated = 0;
}
// forces the buffer to be reallocated with the given size, even if it
// is the same as the current size.
// the requested size must be big enough to hold the valid entries.
// On exit, the valid entries are always contiguous starting at offset 0
// if n==0, frees the buffer
void ForceRealloc(::UInt32 n)
{
LogAssert(n >= m_nEntries);
DrRef<t> *pnew = NULL;
if (n != 0) {
pnew = new DrRef<t>[n];
LogAssert(pnew != NULL);
::UInt32 uFrom = m_uFirstEntry;
for (::UInt32 i = 0; i < m_nEntries; i++) {
pnew[i].TransferFrom(m_prgEntries[uFrom++]);
if (uFrom >= m_nAllocated) {
uFrom = 0;
}
}
}
if (m_prgEntries != NULL) {
delete[] m_prgEntries;
}
m_prgEntries = pnew;
m_nAllocated = n;
m_uFirstEntry = 0;
}
// reallocates the buffer if there are not at least n elements allocated
void GrowTo(::UInt32 n)
{
if (n > m_nAllocated) {
if (n < 2 * m_nAllocated) {
n = 2 * m_nAllocated;
}
if (n < 20) {
n = 20;
}
ForceRealloc(n);
}
}
// Converts a potentially wrapped list (if you have moved the head) into a
// contiguous list, and returns a pointer to the first item in the contiguous list
// If possible, nothing is moved. If the list is wrapped, a new buffer is allocated (easier than moving everything in a full list)
// This operation is always cheap if you never remove from or add to the head.
DrRef<t> *MakeContiguous()
{
if (m_uFirstEntry + m_nEntries > m_nAllocated) {
ForceRealloc(m_nEntries);
}
return m_prgEntries + m_uFirstEntry;
}
UInt32 NumEntries() const
{
return m_nEntries;
}
UInt32 NumAllocated() const
{
return m_nAllocated;
}
DrRef<t>& EntryAt(UInt32 index)
{
LogAssert(index < m_nEntries);
return m_prgEntries[NormalizeEntryIndex(index)];
}
const t *ConstEntryAt(UInt32 index) const
{
LogAssert(index < m_nEntries);
return m_prgEntries[NormalizeEntryIndex(index)];
}
const t *EntryAt(UInt32 index) const
{
ConstEntryAt(index);
}
DrRef<t>& operator[](UInt32 index)
{
return EntryAt(index);
}
const t *operator[](UInt32 index) const
{
return ConstEntryAt(index);
}
// returns NULL if list is empty
t *Head()
{
if (m_nEntries == 0) {
return NULL;
}
return m_prgEntries[m_uFirstEntry];
}
// returns NULL if list is empty
const t *Head() const
{
if (m_nEntries == 0) {
return NULL;
}
return m_prgEntries[m_uFirstEntry];
}
// returns NULL if list is empty
t *Tail()
{
if (m_nEntries == 0) {
return NULL;
}
return EntryAt(m_nEntries-1);
}
// returns NULL if list is empty
const t *Tail() const
{
if (m_nEntries == 0) {
return NULL;
}
return EntryAt(m_nEntries-1);
}
// Invalidates all entry references and pointers previously returned
DrRef<t>& AddEntryToTail(t *pNewEntry = NULL)
{
GrowTo(m_nEntries+1);
DrRef<t>& newEntry = m_prgEntries[NormalizeEntryIndex(m_nEntries++)];
newEntry = pNewEntry;
return newEntry;
}
// Invalidates all entry references and pointers previously returned
DrRef<t>& AddEntryToHead(t *pNewEntry = NULL)
{
GrowTo(m_nEntries+1);
if (m_uFirstEntry == 0) {
m_uFirstEntry = m_nAllocated - 1;
} else {
m_uFirstEntry--;
}
m_nEntries++;
DrRef<t>& newEntry = m_prgEntries[m_uFirstEntry];
newEntry = pNewEntry;
return newEntry;
}
// Invalidates all entry references and pointers previously returned
DrRef<t>& AddEntry(t *pNewEntry = NULL)
{
return AddEntryToTail(pNewEntry);
}
void RemoveEntryFromTail(DrRef<t> *pValOut)
{
LogAssert(m_nEntries != 0);
pValOut->TransferFrom(m_prgEntries[NormalizeEntryIndex(--m_nEntries)]);
}
void RemoveEntryFromHead(DrRef<t> *pValOut)
{
LogAssert(m_nEntries != 0);
pValOut->TransferFrom(m_prgEntries[m_uFirstEntry++]);
if (m_uFirstEntry >= m_nAllocated) {
m_uFirstEntry = 0;
}
m_nEntries--;
}
void Clear()
{
::UInt32 uIndex = m_uFirstEntry;
for (::UInt32 i = 0; i < m_nEntries; i++) {
m_prgEntries[uIndex++] = NULL;
if (uIndex >= m_nAllocated) {
uIndex = 0;
}
}
m_nEntries = 0;
m_uFirstEntry = 0; // might as well reset to the beginning of the array
}
void AddNullEntriesToTail(UInt32 numNulls)
{
if (numNulls != 0)
{
// Depends on the fact that all unused entries are NULL
LogAssert(m_nEntries + numNulls > m_nEntries); // Check for overflow
GrowTo(m_nEntries + numNulls);
m_nEntries += numNulls;
}
}
typedef int (__cdecl *PDRREF_COMPARE_FUNCTION)(void *context, t *p1, t *p2);
typedef struct {
PDRREF_COMPARE_FUNCTION compare;
void *context;
} DrRefSortContext;
static int __cdecl InternalDrRefCompare(void *context, const void *p1, const void *p2)
{
DrRefSortContext *pSortContext = (DrRefSortContext *)context;
return (*pSortContext->compare)(pSortContext->context, *(DrRef<t> *)p1, *(DrRef<t> *)p2);
}
// performs a quicksort on the list, with a user-provided compare function
// Invalidates all entry references and pointers previously returned
void Sort(PDRREF_COMPARE_FUNCTION compare, void * context = NULL)
{
if (m_nEntries > 1) {
DrRef<t> *pFirst = MakeContiguous();
DrRefSortContext ctx;
ctx.compare = compare;
ctx.context = context;
qsort_s(pFirst, (size_t)m_nEntries, sizeof(*pFirst), InternalDrRefCompare, &ctx);
}
}
protected:
UInt32 NormalizeEntryIndex(UInt32 index) const
{
LogAssert(index < m_nAllocated);
if (m_uFirstEntry != 0) {
index += m_uFirstEntry;
if (index >= m_nAllocated) {
index -= m_nAllocated;
}
}
return index;
}
protected:
UInt32 m_uFirstEntry; // Normally 0, the index of the first entry (for circular buffers)
private:
UInt32 m_nEntries;
UInt32 m_nAllocated;
DrRef<t> *m_prgEntries;
};
#endif //end if not defined __DRYADREFCOUNTER_H__