class CTranBufPool : public CBufPoolV
{
struct Handle {
DLINK link;
char* pBuffer;
Handle* pRealHdl;
int nRef;
int nConsBuf;
};
typedef TLinkedList FreeList;
typedef std::map BufferMap;
FreeList m_FreeList;
BufferMap m_BufferMap;
int m_nBlockSize, m_nBufferSize, m_nBlockBase;
int m_nAlloc, m_nMaxBuffers, m_nBuffers, m_nWaterMarks[3];
int m_nMin, m_nMax;
//分配m_nBufferSize字节的内存,该内存空间实际包含m_nAlloc个m_nBlockSize的单元;每个单元与一个Handle相关联,这些Handle入FreeList队列。
bool AllocOnce() {
char* pBuffer = (char*)AlignAlloc(m_nBlockSize, m_nBufferSize);
//记录Block信息
Handle* pHdl = (Handle*)ZeroAlloc(m_nAlloc * sizeof(Handle));
if (pBuffer && pHdl) {
m_BufferMap.insert(BufferMap::value_type(pBuffer, pHdl));
//Buffer数
m_nBuffers += m_nAlloc;
//最后一个Block
pBuffer += m_nBufferSize - m_nBlockSize;
pHdl += m_nAlloc - 1;
for (int i=0; ipBuffer = pBuffer;
pHdl->nRef = 0;
//
pHdl->nConsBuf = i+1;
pHdl->pRealHdl = pHdl;
m_FreeList.push_back(pHdl);
pBuffer -= m_nBlockSize;
pHdl --;
}
return true;
}
if (pBuffer)
free(pBuffer);
if (pHdl)
free(pHdl);
return false;
}
Handle* GetHandle(char* pBuffer) {
BufferMap::iterator it = m_BufferMap.upper_bound(pBuffer);
if (it != m_BufferMap.begin()) {
it --;
char* pHead = it->first;
ASSERT(pHead <= pBuffer);
if (pBuffer < pHead + m_nBufferSize) {
//2^m_nBlockBase
int n = (pBuffer-pHead) >> m_nBlockBase;
Handle* pHdl = it->second + n;
ASSERT(pHdl->pBuffer == pHead + (((uint32)n) << m_nBlockBase));
return pHdl;
}
}
return NULL;
}
void Destroy() {
m_FreeList.Init();
m_nBuffers = 0;
BufferMap::iterator it;
for (it=m_BufferMap.begin(); it!=m_BufferMap.end(); it++) {
free(it->first);
free(it->second);
}
m_BufferMap.clear();
}
public:
CTranBufPool(const char* name, int nCategory) : CBufPoolV(name, nCategory) {
m_nBuffers = 0;
m_nBlockSize = m_nBufferSize = m_nAlloc = m_nMaxBuffers = m_nMin = 0;
m_nWaterMarks[0] = m_nWaterMarks[1] = m_nWaterMarks[2] = 0;
}
bool Create(int nBlockSize, int nAlloc, int nMin, int nMax, double fRatio1, double fRatio2) {
m_nUnitSize = nBlockSize;
m_nBlockSize = nBlockSize;
m_nBlockBase = Log_2(nBlockSize);
if (-1 == m_nBlockBase) {
TRACE0("Fatal: invalid block size of %d\n", nBlockSize);
return false;
}
//Block数
m_nAlloc = nAlloc;
//最多Block数
m_nMaxBuffers = nMax * nAlloc;
//Buffer大小
m_nBufferSize = m_nBlockSize * m_nAlloc;
//当前Block数
m_nBuffers = 0;
//最大buffer数
m_nMax = nMax;
#ifdef _DEBUG
m_nMin = 0;
#else
//最少buffer数
m_nMin = nMin;
#endif
if (0!=fRatio1 && 0!=fRatio2) {
//优先级1水位
m_nWaterMarks[0] = (int)((double)m_nMaxBuffers * fRatio1);
//优先级2水位
m_nWaterMarks[1] = (int)((double)m_nMaxBuffers * fRatio2);
//最大水位
m_nWaterMarks[2] = m_nMaxBuffers-1;
}
for (int i=0; ilink)); \
ASSERT(0 == p->nRef); \
ASSERT(p->pRealHdl == p); \
p->nRef = 1
char* Allocate(uint32 nPriority, int count=1) {
int n;
ASSERT(0 != count);
//最多两次重试
for (int i=0; i<2; i++) {
n = (int)m_FreeList.size();
//使用的Block数大于这个优先级的水位
if (m_nBuffers-n > m_nWaterMarks[nPriority]) {
if (nPriority != 0) {
static time_t last = 0;
time_t now = time(NULL);
//30秒打印一次
if (now - last >= 30) { // avoid too frequent print
//可用的块
int n1 = m_nMaxBuffers-m_nBuffers+n;
//阈值
int n2 = m_nMaxBuffers-m_nWaterMarks[nPriority];
TRACE0("Warning: available tran buf (#%d) touches watermark(#%d, %.f%%)\n",
n1, n2, (double)(n1*100)/m_nMaxBuffers);
last = now;
}
}
return NULL;
}
//够用
if (n >= count) {
Handle *pHdl, *pTmp;
//1个Block的情况
if (1 == count) {
_ALLOC_TRAN_BUF(pHdl, pop_front);
return pHdl->pBuffer;
}
// Big block are formed by multiple consecutive blocks.
// We try from the tail of free list, which brings higher probability.
_ALLOC_TRAN_BUF(pHdl, pop_back);
int i = 1;
//所在buffer后面有足够多的Block
if (pHdl->nConsBuf >= count) {
for ( ; inRef) {
break;
}
m_FreeList.remove(pTmp);
DLINK_INSERT_PREV(&pHdl->link, &pTmp->link);
pTmp->pRealHdl = pHdl;
pTmp->nRef = 1;
}
}
if (i == count) {
#if (defined(_DEBUG) && defined(_UNITTEST))
for (i=0 ; i %p\n", i, pTmp->link._prev, &pTmp->link, pTmp->link._next);
}
#endif
return pHdl->pBuffer;
}
else {
//出错或是空间不够
for (int j=0; jlink);
pTmp->pRealHdl = pTmp;
pTmp->nRef = 0;
m_FreeList.push_front(pTmp);
}
}
}
if (m_nBuffers >= m_nMaxBuffers || !AllocOnce()) {
return NULL;
}
}
return NULL;
}
int AddRef(char* p, bool bCheck=false) {
Handle* pHdl = GetHandle(p);
if (NULL == pHdl) {
if (!bCheck) {
return -1;
}
RaiseError(Invalid_Block);
}
int n = ++ pHdl->pRealHdl->nRef;
ASSERT(2 <= n);
return n;
}
#ifdef _DEBUG
#define _FREE_TRAN_BUF(p, how) \
memset(p->pBuffer, 0xCC, m_nBlockSize); \
m_FreeList.how(p)
#else
#define _FREE_TRAN_BUF(p, how) \
m_FreeList.how(p)
#endif
int Free(char* p, bool bCheck=false) {
Handle* pHdl = GetHandle(p);
if (NULL == pHdl) {
if (bCheck) {
RaiseError(Invalid_Block);
}
return -1;
}
pHdl = pHdl->pRealHdl;
int n = -- pHdl->nRef;
//没有被引用
if (0 == n) {
Handle* pTmp = dlink_get_prev(pHdl);
if (pTmp == pHdl) {
ASSERT_EQUAL(pHdl->pRealHdl, pHdl);
ASSERT_EQUAL(0, pHdl->nRef);
_FREE_TRAN_BUF(pHdl, push_front);
return 0;
}
// here comes big block
Handle* p = pHdl;
do {
pHdl = pTmp;
pTmp = dlink_get_prev(pTmp);
ASSERT_EQUAL(1, pHdl->nRef);
ASSERT_EQUAL(p, pHdl->pRealHdl);
pHdl->pRealHdl = pHdl;
pHdl->nRef = 0;
DLINK_INITIALIZE(&pHdl->link);
_FREE_TRAN_BUF(pHdl, push_back);
} while (p != pTmp);
ASSERT_EQUAL(p, p->pRealHdl);
ASSERT_EQUAL(0, p->nRef);
DLINK_INITIALIZE(&p->link);
_FREE_TRAN_BUF(p, push_back);
return 0;
}
return n;
}
void Print() {
char buf[128];
if (0 != m_nBuffers) {
int nFree = m_nMaxBuffers-m_nBuffers+m_FreeList.size();
PrintSize((uint64)m_nMaxBuffers*m_nBlockSize, buf, sizeof(buf));
TRACE0("%16s: #%d:%d:%d (%.1f%%), Max=%sB\n", m_name, m_FreeList.size(), m_nBuffers, m_nMaxBuffers,
((double)nFree * 100 / m_nMaxBuffers), buf);
}
}
int GetAllocCount() { return m_nBuffers; }
int GetFreeCount() { return m_FreeList.size(); }
void GarbageCollect() {}
void _GarbageCollect() {
if (m_nBuffers!=m_nMin*m_nAlloc && (int)m_FreeList.size()==m_nBuffers) {
Destroy();
Create(m_nBlockSize, m_nAlloc, m_nMin, m_nMax, 0, 0);
}
}
int GetBlockSize() const { return m_nBlockSize; }
/* return the maximum capacity of the pool. */
int GetTotalBufNum() const { return m_nMaxBuffers; }
/* return the allocated buffers' number, including FREED or be USING.
* !!NOTICE: Make sure you know the meaning of 'm_nBuffers' when use.
*/
int GetAllocatedBufNum() const { return m_nBuffers; }
/* return the number of buffers free to use. */
int GetFreeBufNum() const {
return m_nMaxBuffers-m_nBuffers+m_FreeList.size();
}
#undef _ALLOC_TRAN_BUF
#undef _FREE_TRAN_BUF
};
template
class TBufPool : public CBufPoolV
{
uint32 m_nAlloc, m_nCur, m_nMax, m_nStart;
uint32 m_nSize;
TLock m_Lock;
#pragma pack(push, r1, 1)
struct AllocUnit {
SLINK link;
char buf[0];
};
#pragma pack(pop, r1)
//free block链表
TSingleList m_FreeList;
//buffer链表
TSingleList m_Pointers;
bool AllocOnce() {
ASSERT(m_nAlloc != 0);
ASSERT(m_nCur <= m_nMax);
//m_nAlloc个block和一个buffer指针构成
AllocUnit* pUnit = (AllocUnit*)malloc(m_nAlloc * m_nUnitSize + sizeof(SLINK));
#ifdef _DEBUG
memset(pUnit, 0xCC, m_nAlloc * m_nUnitSize + sizeof(SLINK));
#endif
if (pUnit) {
m_Pointers.push(pUnit);
//buffer个数
m_nCur ++;
pUnit = MAKE_PTR(AllocUnit*, pUnit, sizeof(SLINK));
//加入free链
m_FreeList.pushn(m_nAlloc, pUnit, m_nUnitSize);
return true;
}
PERROR("malloc");
return false;
}
public:
TBufPool(char* name, int nCategory, uint32 nSize) : CBufPoolV(name, nCategory) {
m_nAlloc = m_nCur = 0;
m_nMax = (uint32)-1;
m_nSize = nSize;
m_nStart = 0;
}
TBufPool(char* name, int nCategory, uint32 nSize, uint32 nAlloc, uint32 nStart, uint32 nMax=(uint32)-1)
: CBufPoolV(name, nCategory)
{
//数据大小
m_nSize = nSize;
Create(nAlloc, nStart, nMax);
}
virtual ~TBufPool() { Destroy(false); }
bool Create(uint32 nAlloc, uint32 nStart, uint32 nMax=(uint32)-1) {
ASSERT2(nStart, <=, nMax);
ASSERT2(0, !=, nAlloc);
//每块buffer的block个数
m_nAlloc = nAlloc;
m_nMax = nMax;
#ifdef _DEBUG
m_nStart = 0;
m_nUnitSize = m_nSize + sizeof(SLINK) + 4; // signature after each unit
#else
//初始buffer数
m_nStart = nStart;
//节点大小
m_nUnitSize = m_nSize + sizeof(SLINK);
#endif
//当前buf数
for (m_nCur=0; m_nCurbuf, m_nSize) = 0xdeadbeefUL;
#endif
m_Lock.Unlock();
return &pUnit->buf;
}
if (m_nCur>=m_nMax || 0!=i || !AllocOnce()) {
break;
}
}
m_Lock.Unlock();
return NULL;
}
void* ZeroAllocate() {
void* pBuf = Allocate();
if (pBuf) {
ZeroMemory(pBuf, m_nSize);
}
return pBuf;
}
void Free(void* pBuf) {
AllocUnit* pUnit = CONTAINING_RECORD(pBuf, AllocUnit, buf);
#ifdef _DEBUG
ASSERT_EQUAL(*MAKE_PTR(uint32*, pUnit->buf, m_nSize), 0xdeadbeefUL);
memset(pUnit, 0xCC, m_nUnitSize);
#endif
m_Lock.Lock();
m_FreeList.push(pUnit);
m_Lock.Unlock();
}
void PrintObjs() {
TRACE0("Total Objects: ");
m_Lock.Lock();
SLINK* p = (SLINK*)m_Pointers.top();
AllocUnit* pE = NULL;
while(p) {
pE = MAKE_PTR(AllocUnit*, p, sizeof(SLINK));
for(uint32 i = 0; i < m_nAlloc; ++i) {
TRACE0("%p ", pE);
++pE;
}
p = p->_next;
}
TRACE0("\n");
TRACE0("Free Objects: ");
pE = m_FreeList.top();
while(pE) {
TRACE0("%p ", pE);
p = pE->link._next;
pE = p ? CONTAINING_RECORD(p, AllocUnit, link) : NULL;
}
TRACE0("\n");
m_Lock.Unlock();
}
int GetAllocCount() {
return m_nAlloc * m_nCur;
}
int GetFreeCount() {
int m;
m_Lock.Lock();
m = m_FreeList.size();
m_Lock.Unlock();
return m;
}
void GarbageCollect() {
m_Lock.Lock();
if (m_nCur!=m_nStart && (uint32)m_FreeList.size()==m_nAlloc*m_nCur) {
Destroy(false);
Create(m_nAlloc, m_nStart, m_nMax);
}
m_Lock.Unlock();
}
};