Files
hpsocket-linux/common/RingBuffer.h
2025-04-17 20:38:35 +08:00

1732 lines
31 KiB
C++

/*
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
*
* Author : Bruce Liang
* Website : https://github.com/ldcsaa
* Project : https://github.com/ldcsaa/HP-Socket
* Blog : http://www.cnblogs.com/ldcsaa
* Wiki : http://www.oschina.net/p/hp-socket
* QQ Group : 44636872, 75375912
*
* 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
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include "hpsocket/GlobalDef.h"
#include "Singleton.h"
#include "STLHelper.h"
#include "FuncHelper.h"
#include "RWLock.h"
using namespace std;
#define CACHE_LINE 64
#define PACK_SIZE_OF(T) (CACHE_LINE - sizeof(T) % CACHE_LINE)
#if __WORDSIZE == 32
#pragma pack(push, 4)
#endif
// ------------------------------------------------------------------------------------------------------------- //
template <class T, class index_type = DWORD, bool adjust_index = false> class CRingCache
{
public:
enum EnGetResult {GR_FAIL = -1, GR_INVALID = 0, GR_VALID = 1};
typedef T* TPTR;
typedef volatile T* VTPTR;
typedef unordered_set<index_type> IndexSet;
typedef typename IndexSet::const_iterator IndexSetCI;
typedef typename IndexSet::iterator IndexSetI;
static TPTR const E_EMPTY;
static TPTR const E_LOCKED;
static TPTR const E_MAX_STATUS;
public:
static index_type& INDEX_INC(index_type& dwIndex) {if(adjust_index) ++dwIndex; return dwIndex;}
static index_type& INDEX_DEC(index_type& dwIndex) {if(adjust_index) --dwIndex; return dwIndex;}
private:
index_type& INDEX_V2R(index_type& dwIndex) {dwIndex %= m_dwSize; if(dwIndex == 0) dwIndex = m_dwSize; return dwIndex;}
VTPTR& INDEX_VAL(index_type dwIndex) {return *(m_pv + dwIndex);}
public:
BOOL Put(TPTR pElement, index_type& dwIndex)
{
ASSERT(pElement != nullptr);
if(!IsValid()) return FALSE;
BOOL isOK = FALSE;
while(true)
{
if(!HasSpace())
break;
DWORD dwCurSeq = m_dwCurSeq;
index_type dwCurIndex = dwCurSeq % m_dwSize;
VTPTR& pValue = INDEX_VAL(dwCurIndex);
if(pValue == E_EMPTY)
{
if(::InterlockedCompareExchangePointer(&pValue, pElement, E_EMPTY) == E_EMPTY)
{
::InterlockedIncrement(&m_dwCount);
::InterlockedCompareExchange(&m_dwCurSeq, dwCurSeq + 1, dwCurSeq);
dwIndex = INDEX_INC(dwCurIndex);
isOK = TRUE;
if(pElement != E_LOCKED)
EmplaceIndex(dwIndex);
break;
}
}
::InterlockedCompareExchange(&m_dwCurSeq, dwCurSeq + 1, dwCurSeq);
}
return isOK;
}
EnGetResult GetEx(index_type dwIndex, TPTR* ppElement)
{
return Get(INDEX_V2R(dwIndex), ppElement);
}
EnGetResult Get(index_type dwIndex, TPTR* ppElement)
{
ASSERT(dwIndex <= m_dwSize);
ASSERT(ppElement != nullptr);
if(!IsValid() || INDEX_DEC(dwIndex) >= m_dwSize)
{
*ppElement = nullptr;
return GR_FAIL;
}
*ppElement = (TPTR)INDEX_VAL(dwIndex);
return IsValidElement(*ppElement) ? GR_VALID : GR_INVALID;
}
BOOL SetEx(index_type dwIndex, TPTR pElement, TPTR* ppOldElement = nullptr)
{
return Set(INDEX_V2R(dwIndex), pElement, ppOldElement);
}
BOOL Set(index_type dwIndex, TPTR pElement, TPTR* ppOldElement = nullptr)
{
TPTR pElement2 = nullptr;
if(Get(dwIndex, &pElement2) == GR_FAIL)
return FALSE;
if(ppOldElement != nullptr)
*ppOldElement = pElement2;
if(pElement == pElement2)
return FALSE;
int f1 = 0;
int f2 = 0;
if(pElement == E_EMPTY)
{
if(pElement2 == E_LOCKED)
f1 = -1;
else
f1 = f2 = -1;
}
else if(pElement == E_LOCKED)
{
if(pElement2 == E_EMPTY)
f1 = 1;
else
f2 = -1;
}
else
{
if(pElement2 == E_EMPTY)
f1 = f2 = 1;
else if(pElement2 == E_LOCKED)
f2 = 1;
}
BOOL bSetValueFirst = (f1 + f2 >= 0);
index_type dwOuterIndex = dwIndex;
INDEX_DEC(dwIndex);
if(bSetValueFirst) INDEX_VAL(dwIndex) = pElement;
if(f1 > 0) ::InterlockedIncrement(&m_dwCount);
if(f2 != 0) (f2 > 0) ? EmplaceIndex(dwOuterIndex) : EraseIndex(dwOuterIndex);
if(f1 < 0) ::InterlockedDecrement(&m_dwCount);
if(!bSetValueFirst) INDEX_VAL(dwIndex) = pElement;
ASSERT(Spaces() <= Size());
return TRUE;
}
BOOL RemoveEx(index_type dwIndex, TPTR* ppElement = nullptr)
{
return Remove(INDEX_V2R(dwIndex), ppElement);
}
BOOL Remove(index_type dwIndex, TPTR* ppElement = nullptr)
{
return Set(dwIndex, E_EMPTY, ppElement);
}
BOOL AcquireLock(index_type& dwIndex)
{
return Put(E_LOCKED, dwIndex);
}
BOOL ReleaseLock(index_type dwIndex, TPTR pElement)
{
ASSERT(pElement == nullptr || IsValidElement(pElement));
TPTR pElement2 = nullptr;
Get(dwIndex, &pElement2);
ASSERT(pElement2 == E_LOCKED);
if(pElement2 != E_LOCKED)
return FALSE;
return Set(dwIndex, pElement);
}
public:
void Reset(DWORD dwSize = 0)
{
if(IsValid())
Destroy();
if(dwSize > 0)
Create(dwSize);
}
BOOL GetAllElementIndexes(index_type ids[], DWORD& dwCount, BOOL bCopy = TRUE)
{
DWORD dwSize = Elements();
if(ids == nullptr || dwCount == 0)
{
dwCount = dwSize;
return FALSE;
}
if(dwSize == 0)
{
dwCount = 0;
return TRUE;
}
IndexSet* pIndexes = &m_indexes;
if(bCopy)
{
pIndexes = new IndexSet;
CopyIndexes(*pIndexes);
}
DWORD i = 0;
for(auto it = pIndexes->begin(), end = pIndexes->end(); i < dwCount && it != end; ++i, ++it)
ids[i] = *it;
if(bCopy) delete pIndexes;
dwCount = i;
return TRUE;
}
unique_ptr<index_type[]> GetAllElementIndexes(DWORD& dwCount, BOOL bCopy = TRUE)
{
dwCount = (DWORD)m_indexes.size();
unique_ptr<index_type[]> ids(new index_type[dwCount]);
if(dwCount > 0)
GetAllElementIndexes(ids.get(), dwCount, bCopy);
return ids;
}
IndexSet& CopyIndexes(IndexSet& indexes)
{
{
CReadLock locallock(m_cs);
indexes = m_indexes;
}
return indexes;
}
static BOOL IsValidElement(TPTR pElement) {return pElement > E_MAX_STATUS;}
IndexSet& Indexes () { return m_indexes;}
DWORD Size () {return m_dwSize;}
DWORD Elements () {return (DWORD)m_indexes.size();}
DWORD Spaces () {return m_dwSize - m_dwCount;}
BOOL HasSpace () {return m_dwCount < m_dwSize;}
BOOL IsEmpty () {return m_dwCount == 0;}
BOOL IsValid () {return m_pv != nullptr;}
private:
void Create(DWORD dwSize)
{
ASSERT(!IsValid() && dwSize > 0);
m_dwCurSeq = 0;
m_dwCount = 0;
m_dwSize = dwSize;
m_pv = (VTPTR*)malloc(m_dwSize * sizeof(TPTR));
::ZeroMemory(m_pv, m_dwSize * sizeof(TPTR));
}
void Destroy()
{
ASSERT(IsValid());
m_indexes.clear();
free((void*)m_pv);
m_pv = nullptr;
m_dwSize = 0;
m_dwCount = 0;
m_dwCurSeq = 0;
}
void EmplaceIndex(index_type dwIndex)
{
CWriteLock locallock(m_cs);
m_indexes.emplace(dwIndex);
}
void EraseIndex(index_type dwIndex)
{
CWriteLock locallock(m_cs);
m_indexes.erase(dwIndex);
}
public:
CRingCache (DWORD dwSize = 0)
: m_pv (nullptr)
, m_dwSize (0)
, m_dwCount (0)
, m_dwCurSeq(0)
{
Reset(dwSize);
}
~CRingCache()
{
Reset(0);
}
private:
CRingCache(const CRingCache&);
CRingCache operator = (const CRingCache&);
private:
DWORD m_dwSize;
VTPTR* m_pv;
char pack1[PACK_SIZE_OF(VTPTR*)];
volatile DWORD m_dwCurSeq;
char pack2[PACK_SIZE_OF(DWORD)];
volatile DWORD m_dwCount;
char pack3[PACK_SIZE_OF(DWORD)];
CSimpleRWLock m_cs;
IndexSet m_indexes;
};
template <class T, class index_type, bool adjust_index> T* const CRingCache<T, index_type, adjust_index>::E_EMPTY = (T*)0x00;
template <class T, class index_type, bool adjust_index> T* const CRingCache<T, index_type, adjust_index>::E_LOCKED = (T*)0x01;
template <class T, class index_type, bool adjust_index> T* const CRingCache<T, index_type, adjust_index>::E_MAX_STATUS = (T*)0x0F;
// ------------------------------------------------------------------------------------------------------------- //
template <class T, class index_type = DWORD, bool adjust_index = false> class CRingCache2
{
public:
enum EnGetResult {GR_FAIL = -1, GR_INVALID = 0, GR_VALID = 1};
typedef T* TPTR;
typedef volatile T* VTPTR;
typedef unordered_set<index_type> IndexSet;
typedef typename IndexSet::const_iterator IndexSetCI;
typedef typename IndexSet::iterator IndexSetI;
static TPTR const E_EMPTY;
static TPTR const E_LOCKED;
static TPTR const E_MAX_STATUS;
static DWORD const MAX_SIZE;
public:
static index_type& INDEX_INC(index_type& dwIndex) {if(adjust_index) ++dwIndex; return dwIndex;}
static index_type& INDEX_DEC(index_type& dwIndex) {if(adjust_index) --dwIndex; return dwIndex;}
index_type& INDEX_R2V(index_type& dwIndex) {dwIndex += *(m_px + dwIndex) * m_dwSize; return dwIndex;}
BOOL INDEX_V2R(index_type& dwIndex)
{
index_type m = dwIndex % m_dwSize;
BYTE x = *(m_px + m);
if(dwIndex / m_dwSize != x)
return FALSE;
dwIndex = m;
return TRUE;
}
private:
VTPTR& INDEX_VAL(index_type dwIndex) {return *(m_pv + dwIndex);}
public:
BOOL Put(TPTR pElement, index_type& dwIndex)
{
ASSERT(pElement != nullptr);
if(!IsValid()) return FALSE;
BOOL isOK = FALSE;
while(true)
{
if(!HasSpace())
break;
DWORD dwCurSeq = m_dwCurSeq;
index_type dwCurIndex = dwCurSeq % m_dwSize;
VTPTR& pValue = INDEX_VAL(dwCurIndex);
if(pValue == E_EMPTY)
{
if(::InterlockedCompareExchangePointer(&pValue, pElement, E_EMPTY) == E_EMPTY)
{
::InterlockedIncrement(&m_dwCount);
::InterlockedCompareExchange(&m_dwCurSeq, dwCurSeq + 1, dwCurSeq);
dwIndex = INDEX_INC(INDEX_R2V(dwCurIndex));
isOK = TRUE;
if(pElement != E_LOCKED)
EmplaceIndex(dwIndex);
break;
}
}
::InterlockedCompareExchange(&m_dwCurSeq, dwCurSeq + 1, dwCurSeq);
}
return isOK;
}
EnGetResult Get(index_type dwIndex, TPTR* ppElement, index_type* pdwRealIndex = nullptr)
{
ASSERT(ppElement != nullptr);
if(!IsValid() || !INDEX_V2R(INDEX_DEC(dwIndex)))
{
*ppElement = nullptr;
return GR_FAIL;
}
*ppElement = (TPTR)INDEX_VAL(dwIndex);
if(pdwRealIndex) *pdwRealIndex = dwIndex;
return IsValidElement(*ppElement) ? GR_VALID : GR_INVALID;
}
BOOL Set(index_type dwIndex, TPTR pElement, TPTR* ppOldElement = nullptr, index_type* pdwRealIndex = nullptr)
{
TPTR pElement2 = nullptr;
if(pdwRealIndex == nullptr)
pdwRealIndex = CreateLocalObject(index_type);
if(Get(dwIndex, &pElement2, pdwRealIndex) == GR_FAIL)
return FALSE;
if(ppOldElement != nullptr)
*ppOldElement = pElement2;
if(pElement == pElement2)
return FALSE;
int f1 = 0;
int f2 = 0;
if(pElement == E_EMPTY)
{
if(pElement2 == E_LOCKED)
f1 = -1;
else
f1 = f2 = -1;
}
else if(pElement == E_LOCKED)
{
if(pElement2 == E_EMPTY)
f1 = 1;
else
f2 = -1;
}
else
{
if(pElement2 == E_EMPTY)
f1 = f2 = 1;
else if(pElement2 == E_LOCKED)
f2 = 1;
}
BOOL bSetValueFirst = (f1 + f2 >= 0);
index_type dwRealIndex = *pdwRealIndex;
if(bSetValueFirst) INDEX_VAL(dwRealIndex) = pElement;
if(f1 > 0) ::InterlockedIncrement(&m_dwCount);
if(f2 != 0) (f2 > 0) ? EmplaceIndex(dwIndex) : EraseIndex(dwIndex);
if(f1 < 0) {::InterlockedDecrement(&m_dwCount); ++(*(m_px + dwRealIndex));}
if(!bSetValueFirst) INDEX_VAL(dwRealIndex) = pElement;
ASSERT(Spaces() <= Size());
return TRUE;
}
BOOL Remove(index_type dwIndex, TPTR* ppElement = nullptr)
{
return Set(dwIndex, E_EMPTY, ppElement);
}
BOOL AcquireLock(index_type& dwIndex)
{
return Put(E_LOCKED, dwIndex);
}
BOOL ReleaseLock(index_type dwIndex, TPTR pElement)
{
ASSERT(pElement == nullptr || IsValidElement(pElement));
TPTR pElement2 = nullptr;
Get(dwIndex, &pElement2);
ASSERT(pElement2 == E_LOCKED);
if(pElement2 != E_LOCKED)
return FALSE;
return Set(dwIndex, pElement);
}
public:
void Reset(DWORD dwSize = 0)
{
if(IsValid())
Destroy();
if(dwSize > 0)
Create(dwSize);
}
BOOL GetAllElementIndexes(index_type ids[], DWORD& dwCount, BOOL bCopy = TRUE)
{
DWORD dwSize = Elements();
if(ids == nullptr || dwCount == 0)
{
dwCount = dwSize;
return FALSE;
}
if(dwSize == 0)
{
dwCount = 0;
return TRUE;
}
IndexSet* pIndexes = &m_indexes;
if(bCopy)
{
pIndexes = new IndexSet;
CopyIndexes(*pIndexes);
}
DWORD i = 0;
for(auto it = pIndexes->begin(), end = pIndexes->end(); i < dwCount && it != end; ++i, ++it)
ids[i] = *it;
if(bCopy) delete pIndexes;
dwCount = i;
return TRUE;
}
unique_ptr<index_type[]> GetAllElementIndexes(DWORD& dwCount, BOOL bCopy = TRUE)
{
dwCount = (DWORD)m_indexes.size();
unique_ptr<index_type[]> ids(new index_type[dwCount]);
if(dwCount > 0)
GetAllElementIndexes(ids.get(), dwCount, bCopy);
return ids;
}
IndexSet& CopyIndexes(IndexSet& indexes)
{
{
CReadLock locallock(m_cs);
indexes = m_indexes;
}
return indexes;
}
static BOOL IsValidElement(TPTR pElement) {return pElement > E_MAX_STATUS;}
IndexSet& Indexes () {return m_indexes;}
DWORD Size () {return m_dwSize;}
DWORD Elements () {return (DWORD)m_indexes.size();}
DWORD Spaces () {return m_dwSize - m_dwCount;}
BOOL HasSpace () {return m_dwCount < m_dwSize;}
BOOL IsEmpty () {return m_dwCount == 0;}
BOOL IsValid () {return m_pv != nullptr;}
private:
void Create(DWORD dwSize)
{
ASSERT(!IsValid() && dwSize > 0 && dwSize <= MAX_SIZE);
m_dwCurSeq = 0;
m_dwCount = 0;
m_dwSize = dwSize;
m_pv = (VTPTR*)malloc(m_dwSize * sizeof(TPTR));
m_px = (BYTE*)malloc(m_dwSize * sizeof(BYTE));
::ZeroMemory(m_pv, m_dwSize * sizeof(TPTR));
::ZeroMemory(m_px, m_dwSize * sizeof(BYTE));
}
void Destroy()
{
ASSERT(IsValid());
m_indexes.clear();
free((void*)m_pv);
free((void*)m_px);
m_pv = nullptr;
m_px = nullptr;
m_dwSize = 0;
m_dwCount = 0;
m_dwCurSeq = 0;
}
void EmplaceIndex(index_type dwIndex)
{
CWriteLock locallock(m_cs);
m_indexes.emplace(dwIndex);
}
void EraseIndex(index_type dwIndex)
{
CWriteLock locallock(m_cs);
m_indexes.erase(dwIndex);
}
public:
CRingCache2 (DWORD dwSize = 0)
: m_pv (nullptr)
, m_px (nullptr)
, m_dwSize (0)
, m_dwCount (0)
, m_dwCurSeq(0)
{
Reset(dwSize);
}
~CRingCache2()
{
Reset(0);
}
DECLARE_NO_COPY_CLASS(CRingCache2)
private:
DWORD m_dwSize;
VTPTR* m_pv;
char pack1[PACK_SIZE_OF(VTPTR*)];
BYTE* m_px;
char pack2[PACK_SIZE_OF(BYTE*)];
volatile DWORD m_dwCurSeq;
char pack3[PACK_SIZE_OF(DWORD)];
volatile DWORD m_dwCount;
char pack4[PACK_SIZE_OF(DWORD)];
CSimpleRWLock m_cs;
IndexSet m_indexes;
};
template <class T, class index_type, bool adjust_index> T* const CRingCache2<T, index_type, adjust_index>::E_EMPTY = (T*)0x00;
template <class T, class index_type, bool adjust_index> T* const CRingCache2<T, index_type, adjust_index>::E_LOCKED = (T*)0x01;
template <class T, class index_type, bool adjust_index> T* const CRingCache2<T, index_type, adjust_index>::E_MAX_STATUS = (T*)0x0F;
template <class T, class index_type, bool adjust_index> DWORD const CRingCache2<T, index_type, adjust_index>::MAX_SIZE =
#if __WORDSIZE == 32
0x00FFFFFF
#else
0xFFFFFFFF
#endif
;
// ------------------------------------------------------------------------------------------------------------- //
template <class T> class CRingPool
{
private:
typedef T* TPTR;
typedef volatile T* VTPTR;
static TPTR const E_EMPTY;
static TPTR const E_LOCKED;
static TPTR const E_MAX_STATUS;
private:
VTPTR& INDEX_VAL(DWORD dwIndex) {return *(m_pv + dwIndex);}
public:
BOOL TryPut(TPTR pElement)
{
ASSERT(pElement != nullptr);
if(!IsValid()) return FALSE;
BOOL isOK = FALSE;
for(DWORD i = 0; i < m_dwSize; i++)
{
DWORD seqPut = m_seqPut;
if(!HasPutSpace(seqPut))
break;
DWORD dwIndex = seqPut % m_dwSize;
VTPTR& pValue = INDEX_VAL(dwIndex);
TPTR pCurrent = (TPTR)pValue;
if(pCurrent == E_EMPTY)
{
if(::InterlockedCompareExchangePointer(&pValue, pElement, pCurrent) == pCurrent)
{
::InterlockedCompareExchange(&m_seqPut, seqPut + 1, seqPut);
isOK = TRUE;
break;
}
}
::InterlockedCompareExchange(&m_seqPut, seqPut + 1, seqPut);
}
return isOK;
}
BOOL TryGet(TPTR* ppElement)
{
ASSERT(ppElement != nullptr);
if(!IsValid()) return FALSE;
BOOL isOK = FALSE;
while(true)
{
DWORD seqGet = m_seqGet;
if(!HasGetSpace(seqGet))
break;
DWORD dwIndex = seqGet % m_dwSize;
VTPTR& pValue = INDEX_VAL(dwIndex);
TPTR pCurrent = (TPTR)pValue;
if(pCurrent > E_MAX_STATUS)
{
if(::InterlockedCompareExchangePointer(&pValue, E_EMPTY, pCurrent) == pCurrent)
{
::InterlockedCompareExchange(&m_seqGet, seqGet + 1, seqGet);
*(ppElement) = pCurrent;
isOK = TRUE;
break;
}
}
::InterlockedCompareExchange(&m_seqGet, seqGet + 1, seqGet);
}
return isOK;
}
BOOL TryLock(TPTR* ppElement, DWORD& dwIndex)
{
ASSERT(ppElement != nullptr);
if(!IsValid()) return FALSE;
BOOL isOK = FALSE;
while(true)
{
DWORD seqGet = m_seqGet;
if(!HasGetSpace(seqGet))
break;
dwIndex = seqGet % m_dwSize;
VTPTR& pValue = INDEX_VAL(dwIndex);
TPTR pCurrent = (TPTR)pValue;
if(pCurrent > E_MAX_STATUS)
{
if(::InterlockedCompareExchangePointer(&pValue, E_LOCKED, pCurrent) == pCurrent)
{
::InterlockedCompareExchange(&m_seqGet, seqGet + 1, seqGet);
*(ppElement) = pCurrent;
isOK = TRUE;
break;
}
}
::InterlockedCompareExchange(&m_seqGet, seqGet + 1, seqGet);
}
return isOK;
}
BOOL ReleaseLock(TPTR pElement, DWORD dwIndex)
{
ASSERT(dwIndex < m_dwSize);
ASSERT(pElement == nullptr || pElement > E_MAX_STATUS);
if(!IsValid()) return FALSE;
VTPTR& pValue = INDEX_VAL(dwIndex);
ENSURE(pValue == E_LOCKED);
if(pElement == nullptr)
pValue = E_EMPTY;
else
pValue = pElement;
return TRUE;
}
public:
void Reset(DWORD dwSize = 0)
{
if(IsValid())
Destroy();
if(dwSize > 0)
Create(dwSize);
}
void Clear()
{
for(DWORD dwIndex = 0; dwIndex < m_dwSize; dwIndex++)
{
VTPTR& pValue = INDEX_VAL(dwIndex);
if(pValue > E_MAX_STATUS)
{
T::Destruct((TPTR)pValue);
pValue = E_EMPTY;
}
}
Reset();
}
DWORD Size() {return m_dwSize;}
DWORD Elements() {return m_seqPut - m_seqGet;}
BOOL IsFull() {return Elements() == Size();}
BOOL IsEmpty() {return Elements() == 0;}
BOOL IsValid() {return m_pv != nullptr;}
private:
BOOL HasPutSpace(DWORD seqPut)
{
return ((int)(seqPut - m_seqGet) < (int)m_dwSize);
}
BOOL HasGetSpace(DWORD seqGet)
{
return ((int)(m_seqPut - seqGet) > 0);
}
void Create(DWORD dwSize)
{
ASSERT(!IsValid() && dwSize > 0);
m_seqPut = 0;
m_seqGet = 0;
m_dwSize = dwSize;
m_pv = (VTPTR*)malloc(m_dwSize * sizeof(TPTR));
::ZeroMemory(m_pv, m_dwSize * sizeof(TPTR));
}
void Destroy()
{
ASSERT(IsValid());
free((void*)m_pv);
m_pv = nullptr;
m_dwSize = 0;
m_seqPut = 0;
m_seqGet = 0;
}
public:
CRingPool(DWORD dwSize = 0)
: m_pv(nullptr)
, m_dwSize(0)
, m_seqPut(0)
, m_seqGet(0)
{
Reset(dwSize);
}
~CRingPool()
{
Reset(0);
}
private:
CRingPool(const CRingPool&);
CRingPool operator = (const CRingPool&);
private:
DWORD m_dwSize;
VTPTR* m_pv;
char pack1[PACK_SIZE_OF(VTPTR*)];
volatile DWORD m_seqPut;
char pack2[PACK_SIZE_OF(DWORD)];
volatile DWORD m_seqGet;
char pack3[PACK_SIZE_OF(DWORD)];
};
template <class T> T* const CRingPool<T>::E_EMPTY = (T*)0x00;
template <class T> T* const CRingPool<T>::E_LOCKED = (T*)0x01;
template <class T> T* const CRingPool<T>::E_MAX_STATUS = (T*)0x0F;
// ------------------------------------------------------------------------------------------------------------- //
template <class T> class CCASQueueX
{
private:
struct Node;
typedef Node* NPTR;
typedef volatile Node* VNPTR;
typedef volatile UINT VUINT;
struct Node
{
T* pValue;
VNPTR pNext;
Node(T* val, NPTR next = nullptr)
: pValue(val), pNext(next)
{
}
};
public:
void PushBack(T* pVal)
{
ASSERT(pVal != nullptr);
VNPTR pTail = nullptr;
NPTR pNode = new Node(pVal);
while(true)
{
pTail = m_pTail;
if(::InterlockedCompareExchangePointer(&m_pTail, pNode, pTail) == pTail)
{
pTail->pNext = pNode;
break;
}
}
::InterlockedIncrement(&m_iSize);
}
void UnsafePushBack(T* pVal)
{
ASSERT(pVal != nullptr);
NPTR pNode = new Node(pVal);
m_pTail->pNext = pNode;
m_pTail = pNode;
::InterlockedIncrement(&m_iSize);
}
BOOL PopFront(T** ppVal)
{
ASSERT(ppVal != nullptr);
if(IsEmpty())
return FALSE;
BOOL isOK = FALSE;
NPTR pHead = nullptr;
NPTR pNext = nullptr;
while(true)
{
Lock();
pHead = (NPTR)m_pHead;
pNext = (NPTR)pHead->pNext;
if(pNext == nullptr)
{
Unlock();
break;
}
*ppVal = pNext->pValue;
m_pHead = pNext;
Unlock();
isOK = TRUE;
::InterlockedDecrement(&m_iSize);
delete pHead;
break;
}
return isOK;
}
BOOL UnsafePopFront(T** ppVal)
{
if(!UnsafePeekFront(ppVal))
return FALSE;
UnsafePopFrontNotCheck();
return TRUE;
}
BOOL UnsafePeekFront(T** ppVal)
{
ASSERT(ppVal != nullptr);
NPTR pNext = (NPTR)m_pHead->pNext;
if(pNext == nullptr)
return FALSE;
*ppVal = pNext->pValue;
return TRUE;
}
void UnsafePopFrontNotCheck()
{
NPTR pHead = (NPTR)m_pHead;
NPTR pNext = (NPTR)pHead->pNext;
m_pHead = pNext;
::InterlockedDecrement(&m_iSize);
delete pHead;
}
void UnsafeClear()
{
ASSERT(m_pHead != nullptr);
m_dwCheckTime = 0;
while(m_pHead->pNext != nullptr)
UnsafePopFrontNotCheck();
}
public:
UINT Size() {return m_iSize;}
BOOL IsEmpty() {return m_iSize == 0;}
void Lock() {while(!TryLock()) ::YieldProcessor();}
void Unlock() {m_iLock = 0;}
BOOL TryLock() {return (::InterlockedCompareExchange(&m_iLock, 1u, 0u) == 0);}
DWORD GetCheckTime()
{
return m_dwCheckTime;
}
void UpdateCheckTime(DWORD dwCurrent = 0)
{
if(dwCurrent == 0)
dwCurrent = ::TimeGetTime();
m_dwCheckTime = dwCurrent;
}
int GetCheckTimeGap(DWORD dwCurrent = 0)
{
int rs = (int)GetTimeGap32(m_dwCheckTime, dwCurrent);
if(rs < -60 * 1000)
rs = MAXINT;
return rs;
}
public:
CCASQueueX() : m_iLock(0), m_iSize(0), m_dwCheckTime(0)
{
m_pHead = m_pTail = new Node(nullptr);
}
~CCASQueueX()
{
ASSERT(m_iLock == 0);
ASSERT(m_iSize == 0);
ASSERT(m_pTail == m_pHead);
ASSERT(m_pHead != nullptr);
ASSERT(m_pHead->pNext == nullptr);
UnsafeClear();
delete m_pHead;
}
DECLARE_NO_COPY_CLASS(CCASQueueX)
private:
VUINT m_iLock;
VUINT m_iSize;
VNPTR m_pHead;
VNPTR m_pTail;
volatile DWORD m_dwCheckTime;
};
template <class T> class CCASSimpleQueueX
{
private:
struct Node;
typedef Node* NPTR;
typedef volatile Node* VNPTR;
typedef volatile UINT VUINT;
struct Node
{
T tValue;
VNPTR pNext;
Node(T val, NPTR next = nullptr)
: tValue(val), pNext(next)
{
}
};
public:
void PushBack(T tVal)
{
VNPTR pTail = nullptr;
NPTR pNode = new Node(tVal);
while(true)
{
pTail = m_pTail;
if(::InterlockedCompareExchangePointer(&m_pTail, pNode, pTail) == pTail)
{
pTail->pNext = pNode;
break;
}
}
::InterlockedIncrement(&m_iSize);
}
void UnsafePushBack(T tVal)
{
NPTR pNode = new Node(tVal);
m_pTail->pNext = pNode;
m_pTail = pNode;
::InterlockedIncrement(&m_iSize);
}
BOOL PopFront(T* ptVal)
{
ASSERT(ptVal != nullptr);
if(IsEmpty())
return FALSE;
BOOL isOK = FALSE;
NPTR pHead = nullptr;
NPTR pNext = nullptr;
while(true)
{
Lock();
pHead = (NPTR)m_pHead;
pNext = (NPTR)pHead->pNext;
if(pNext == nullptr)
{
Unlock();
break;
}
*ptVal = pNext->tValue;
m_pHead = pNext;
Unlock();
isOK = TRUE;
::InterlockedDecrement(&m_iSize);
delete pHead;
break;
}
return isOK;
}
BOOL UnsafePopFront(T* ptVal)
{
if(!UnsafePeekFront(ptVal))
return FALSE;
UnsafePopFrontNotCheck();
return TRUE;
}
BOOL UnsafePeekFront(T* ptVal)
{
ASSERT(ptVal != nullptr);
NPTR pNext = (NPTR)m_pHead->pNext;
if(pNext == nullptr)
return FALSE;
*ptVal = pNext->pValue;
return TRUE;
}
void UnsafePopFrontNotCheck()
{
NPTR pHead = (NPTR)m_pHead;
NPTR pNext = (NPTR)pHead->pNext;
m_pHead = pNext;
::InterlockedDecrement(&m_iSize);
delete pHead;
}
void UnsafeClear()
{
ASSERT(m_pHead != nullptr);
m_dwCheckTime = 0;
while(m_pHead->pNext != nullptr)
UnsafePopFrontNotCheck();
}
public:
UINT Size() {return m_iSize;}
BOOL IsEmpty() {return m_iSize == 0;}
void Lock() {while(!TryLock()) ::YieldProcessor();}
void Unlock() {m_iLock = 0;}
BOOL TryLock() {return (::InterlockedCompareExchange(&m_iLock, 1u, 0u) == 0);}
DWORD GetCheckTime()
{
return m_dwCheckTime;
}
void UpdateCheckTime(DWORD dwCurrent = 0)
{
if(dwCurrent == 0)
dwCurrent = ::TimeGetTime();
m_dwCheckTime = dwCurrent;
}
int GetCheckTimeGap(DWORD dwCurrent = 0)
{
int rs = (int)GetTimeGap32(m_dwCheckTime, dwCurrent);
if(rs < -60 * 1000)
rs = MAXINT;
return rs;
}
public:
CCASSimpleQueueX() : m_iLock(0), m_iSize(0), m_dwCheckTime(0)
{
m_pHead = m_pTail = new Node(0);
}
~CCASSimpleQueueX()
{
ASSERT(m_iLock == 0);
ASSERT(m_iSize == 0);
ASSERT(m_pTail == m_pHead);
ASSERT(m_pHead != nullptr);
ASSERT(m_pHead->pNext == nullptr);
UnsafeClear();
delete m_pHead;
}
DECLARE_NO_COPY_CLASS(CCASSimpleQueueX)
private:
VUINT m_iLock;
VUINT m_iSize;
VNPTR m_pHead;
VNPTR m_pTail;
volatile DWORD m_dwCheckTime;
};
template <class T> class CCASQueueY
{
public:
void PushBack(T* pVal)
{
CCriSecLock locallock(m_csGuard);
UnsafePushBack(pVal);
}
void UnsafePushBack(T* pVal)
{
ASSERT(pVal != nullptr);
m_lsItems.push_back(pVal);
}
void PushFront(T* pVal)
{
CCriSecLock locallock(m_csGuard);
UnsafePushFront(pVal);
}
void UnsafePushFront(T* pVal)
{
ASSERT(pVal != nullptr);
m_lsItems.push_front(pVal);
}
BOOL PopFront(T** ppVal)
{
CCriSecLock locallock(m_csGuard);
return UnsafePopFront(ppVal);
}
BOOL UnsafePopFront(T** ppVal)
{
if(!UnsafePeekFront(ppVal))
return FALSE;
UnsafePopFrontNotCheck();
return TRUE;
}
BOOL PeekFront(T** ppVal)
{
CCriSecLock locallock(m_csGuard);
return UnsafePeekFront(ppVal);
}
BOOL UnsafePeekFront(T** ppVal)
{
ASSERT(ppVal != nullptr);
if(m_lsItems.empty())
return FALSE;
*ppVal = m_lsItems.front();
return TRUE;
}
void UnsafePopFrontNotCheck()
{
m_lsItems.pop_front();
}
void Clear()
{
CCriSecLock locallock(m_csGuard);
UnsafeClear();
}
void UnsafeClear()
{
m_dwCheckTime = 0;
m_lsItems.clear();
}
public:
ULONG Size() {return (ULONG)m_lsItems.size();}
BOOL IsEmpty() {return (BOOL)m_lsItems.empty();}
void Lock() {m_csGuard.lock();}
void Unlock() {m_csGuard.unlock();}
BOOL TryLock() {return m_csGuard.try_lock();}
CCriSec& Guard(){return m_csGuard;}
DWORD GetCheckTime()
{
return m_dwCheckTime;
}
void UpdateCheckTime(DWORD dwCurrent = 0)
{
if(dwCurrent == 0)
dwCurrent = ::TimeGetTime();
m_dwCheckTime = dwCurrent;
}
int GetCheckTimeGap(DWORD dwCurrent = 0)
{
int rs = (int)GetTimeGap32(m_dwCheckTime, dwCurrent);
if(rs < -60 * 1000)
rs = MAXINT;
return rs;
}
public:
CCASQueueY()
: m_dwCheckTime(0)
{
}
~CCASQueueY()
{
ASSERT(IsEmpty());
UnsafeClear();
}
DECLARE_NO_COPY_CLASS(CCASQueueY)
private:
CCriSec m_csGuard;
deque<T*> m_lsItems;
volatile DWORD m_dwCheckTime;
};
template <class T> class CCASSimpleQueueY
{
public:
void PushBack(T tVal)
{
CCriSecLock locallock(m_csGuard);
UnsafePushBack(tVal);
}
void UnsafePushBack(T tVal)
{
m_lsItems.push_back(tVal);
}
void PushFront(T tVal)
{
CCriSecLock locallock(m_csGuard);
UnsafePushFront(tVal);
}
void UnsafePushFront(T tVal)
{
m_lsItems.push_front(tVal);
}
BOOL PopFront(T* ptVal)
{
CCriSecLock locallock(m_csGuard);
return UnsafePopFront(ptVal);
}
BOOL UnsafePopFront(T* ptVal)
{
if(!UnsafePeekFront(ptVal))
return FALSE;
UnsafePopFrontNotCheck();
return TRUE;
}
BOOL PeekFront(T* ptVal)
{
CCriSecLock locallock(m_csGuard);
return UnsafePeekFront(ptVal);
}
BOOL UnsafePeekFront(T* ptVal)
{
ASSERT(ptVal != nullptr);
if(m_lsItems.empty())
return FALSE;
*ptVal = m_lsItems.front();
return TRUE;
}
void UnsafePopFrontNotCheck()
{
m_lsItems.pop_front();
}
void Clear()
{
CCriSecLock locallock(m_csGuard);
UnsafeClear();
}
void UnsafeClear()
{
m_dwCheckTime = 0;
m_lsItems.clear();
}
public:
ULONG Size() {return (ULONG)m_lsItems.size();}
BOOL IsEmpty() {return (BOOL)m_lsItems.empty();}
void Lock() {m_csGuard.lock();}
void Unlock() {m_csGuard.unlock();}
BOOL TryLock() {return m_csGuard.try_lock();}
CCriSec& Guard(){return m_csGuard;}
DWORD GetCheckTime()
{
return m_dwCheckTime;
}
void UpdateCheckTime(DWORD dwCurrent = 0)
{
if(dwCurrent == 0)
dwCurrent = ::TimeGetTime();
m_dwCheckTime = dwCurrent;
}
int GetCheckTimeGap(DWORD dwCurrent = 0)
{
int rs = (int)GetTimeGap32(m_dwCheckTime, dwCurrent);
if(rs < -60 * 1000)
rs = MAXINT;
return rs;
}
public:
CCASSimpleQueueY()
: m_dwCheckTime(0)
{
}
~CCASSimpleQueueY()
{
ASSERT(IsEmpty());
UnsafeClear();
}
DECLARE_NO_COPY_CLASS(CCASSimpleQueueY)
private:
CCriSec m_csGuard;
deque<T> m_lsItems;
volatile DWORD m_dwCheckTime;
};
template <class T> using CCASQueue = CCASQueueX<T>;
template <class T> using CCASSimpleQueue = CCASSimpleQueueX<T>;
template<typename T>
void ReleaseGCObj(CCASQueue<T>& lsGC, DWORD dwLockTime, BOOL bForce = FALSE)
{
static const int MIN_CHECK_INTERVAL = 1 * 1000;
static const int MAX_CHECK_INTERVAL = 15 * 1000;
T* pObj = nullptr;
if(bForce)
{
CLocalLock<CCASQueue<T>> locallock(lsGC);
while(lsGC.UnsafePeekFront(&pObj))
{
lsGC.UnsafePopFrontNotCheck();
T::Destruct(pObj);
}
}
else
{
if(lsGC.IsEmpty() || lsGC.GetCheckTimeGap() < MAX(MIN((int)(dwLockTime / 3), MAX_CHECK_INTERVAL), MIN_CHECK_INTERVAL))
return;
T* pFirst = nullptr;
BOOL bFirst = TRUE;
DWORD now = 0;
while(TRUE)
{
ASSERT((pObj = nullptr) == nullptr);
{
CLocalTryLock<CCASQueue<T>> locallock(lsGC);
if(!locallock.IsValid())
break;
if(bFirst)
{
bFirst = FALSE;
now = ::TimeGetTime();
lsGC.UpdateCheckTime(now);
}
if(!lsGC.UnsafePeekFront(&pObj))
break;
if((int)(now - pObj->GetFreeTime()) < (int)dwLockTime)
break;
lsGC.UnsafePopFrontNotCheck();
if(pObj->GetCount() > 0)
{
lsGC.PushBack(pObj);
if(pFirst == nullptr)
pFirst = pObj;
else if(pFirst == pObj)
break;
continue;
}
}
ASSERT(pObj != nullptr);
T::Destruct(pObj);
}
}
}
#if __WORDSIZE == 32
#pragma pack(pop)
#endif