mirror of
https://github.com/Mauler125/r5sdk.git
synced 2025-02-09 19:15:03 +01:00
Tier1: reimplement CUtlMemoryPool
Class is slightly modified; it uses 2 CBlob pointers instead of a single CBlob member. CUtlMemoryPool::m_pHeadOfFreeList is also replaced with the new CUtlMemoryPool::m_pPrev pointer. Code has been modified to accommodate this change and aligns with the assembly code of r5.
This commit is contained in:
parent
bd230506fb
commit
5721ffd22b
@ -14,22 +14,644 @@
|
||||
#ifndef MEMPOOL_H
|
||||
#define MEMPOOL_H
|
||||
|
||||
struct CUtlMemoryPool
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "tier0/memalloc.h"
|
||||
#include "tier0/tslist.h"
|
||||
#include "tier0/platform.h"
|
||||
#include "tier1/utlvector.h"
|
||||
#include "tier1/utlrbtree.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Optimized pool memory allocator
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
typedef void (*MemoryPoolReportFunc_t)(PRINTF_FORMAT_STRING char const* pMsg, ...);
|
||||
|
||||
class CUtlMemoryPool
|
||||
{
|
||||
public:
|
||||
// Ways the memory pool can grow when it needs to make a new blob.
|
||||
enum MemoryPoolGrowType_t
|
||||
{
|
||||
GROW_NONE = 0, // Don't allow new blobs.
|
||||
GROW_FAST = 1, // New blob size is numElements * (i+1) (ie: the blocks it allocates
|
||||
// get larger and larger each time it allocates one).
|
||||
GROW_SLOW = 2 // New blob size is numElements.
|
||||
};
|
||||
|
||||
CUtlMemoryPool(int blockSize, int numElements, int growMode = GROW_FAST, const char* pszAllocOwner = NULL, unsigned short nAlignment = 0);
|
||||
~CUtlMemoryPool();
|
||||
|
||||
void* Alloc(); // Allocate the element size you specified in the constructor.
|
||||
void* Alloc(size_t amount);
|
||||
void* AllocZero(); // Allocate the element size you specified in the constructor, zero the memory before construction
|
||||
void* AllocZero(size_t amount);
|
||||
void Free(void* pMem);
|
||||
|
||||
// Frees everything
|
||||
void Clear();
|
||||
|
||||
// Error reporting...
|
||||
static void SetErrorReportFunc(MemoryPoolReportFunc_t func);
|
||||
|
||||
// returns number of allocated blocks
|
||||
int Count() const { return m_BlocksAllocated; }
|
||||
int PeakCount() const { return m_PeakAlloc; }
|
||||
int BlockSize() const { return m_BlockSize; }
|
||||
int Size() const;
|
||||
|
||||
bool IsAllocationWithinPool(void* pMem) const;
|
||||
|
||||
protected:
|
||||
class CBlob
|
||||
{
|
||||
public:
|
||||
short m_nAlignment; // to int align the struct.
|
||||
short m_NumBlobs; // Number of blobs.
|
||||
const char* m_pszAllocOwner;
|
||||
CBlob* m_pPrev, * m_pNext;
|
||||
int m_NumBytes; // Number of bytes in this blob.
|
||||
char m_Data[1];
|
||||
char m_Padding[3]; // to int align the struct
|
||||
};
|
||||
|
||||
int m_BlockSize;
|
||||
int m_BlocksPerBlob;
|
||||
int m_GrowMode;
|
||||
int m_BlocksAllocated;
|
||||
CBlob m_BlobHead;
|
||||
// Resets the pool
|
||||
void Init();
|
||||
void AddNewBlob();
|
||||
void ReportLeaks();
|
||||
|
||||
int m_BlockSize;
|
||||
int m_BlocksPerBlob;
|
||||
|
||||
int m_GrowMode; // GROW_ enum.
|
||||
|
||||
int m_BlocksAllocated;
|
||||
int m_PeakAlloc;
|
||||
unsigned short m_nAlignment;
|
||||
unsigned short m_NumBlobs;
|
||||
|
||||
const char* m_pszAllocOwner;
|
||||
|
||||
CUtlMemoryPool::CBlob* m_pPrev;
|
||||
CUtlMemoryPool::CBlob* m_pNext;
|
||||
|
||||
static MemoryPoolReportFunc_t g_ReportFunc;
|
||||
};
|
||||
|
||||
#endif // MEMPOOL_H
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Multi-thread/Thread Safe Memory Class
|
||||
//-----------------------------------------------------------------------------
|
||||
class CMemoryPoolMT : public CUtlMemoryPool
|
||||
{
|
||||
public:
|
||||
CMemoryPoolMT(int blockSize, int numElements, int growMode = GROW_FAST, const char* pszAllocOwner = NULL, unsigned short nAlignment = 0) : CUtlMemoryPool(blockSize, numElements, growMode, pszAllocOwner, nAlignment) {}
|
||||
|
||||
|
||||
void* Alloc() { AUTO_LOCK(m_mutex); return CUtlMemoryPool::Alloc(); }
|
||||
void* Alloc(size_t amount) { AUTO_LOCK(m_mutex); return CUtlMemoryPool::Alloc(amount); }
|
||||
void* AllocZero() { AUTO_LOCK(m_mutex); return CUtlMemoryPool::AllocZero(); }
|
||||
void* AllocZero(size_t amount) { AUTO_LOCK(m_mutex); return CUtlMemoryPool::AllocZero(amount); }
|
||||
void Free(void* pMem) { AUTO_LOCK(m_mutex); CUtlMemoryPool::Free(pMem); }
|
||||
|
||||
// Frees everything
|
||||
void Clear() { AUTO_LOCK(m_mutex); return CUtlMemoryPool::Clear(); }
|
||||
private:
|
||||
CThreadFastMutex m_mutex; // @TODO: Rework to use tslist (toml 7/6/2007)
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Wrapper macro to make an allocator that returns particular typed allocations
|
||||
// and construction and destruction of objects.
|
||||
//-----------------------------------------------------------------------------
|
||||
template< class T >
|
||||
class CClassMemoryPool : public CUtlMemoryPool
|
||||
{
|
||||
public:
|
||||
CClassMemoryPool(int numElements, int growMode = GROW_FAST, int nAlignment = 0) :
|
||||
CUtlMemoryPool(sizeof(T), numElements, growMode, MEM_ALLOC_CLASSNAME(T), nAlignment) {}
|
||||
|
||||
T* Alloc();
|
||||
T* AllocZero();
|
||||
void Free(T* pMem);
|
||||
|
||||
void Clear();
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Specialized pool for aligned data management (e.g., Xbox textures)
|
||||
//-----------------------------------------------------------------------------
|
||||
template <int ITEM_SIZE, int ALIGNMENT, int CHUNK_SIZE, class CAllocator, bool GROWMODE = false, int COMPACT_THRESHOLD = 4 >
|
||||
class CAlignedMemPool
|
||||
{
|
||||
enum
|
||||
{
|
||||
BLOCK_SIZE = COMPILETIME_MAX(ALIGN_VALUE(ITEM_SIZE, ALIGNMENT), 8),
|
||||
};
|
||||
|
||||
public:
|
||||
CAlignedMemPool();
|
||||
|
||||
void* Alloc();
|
||||
void Free(void* p);
|
||||
|
||||
static int __cdecl CompareChunk(void* const* ppLeft, void* const* ppRight);
|
||||
void Compact();
|
||||
|
||||
int NumTotal() { AUTO_LOCK(m_mutex); return m_Chunks.Count() * (CHUNK_SIZE / BLOCK_SIZE); }
|
||||
int NumAllocated() { AUTO_LOCK(m_mutex); return NumTotal() - m_nFree; }
|
||||
int NumFree() { AUTO_LOCK(m_mutex); return m_nFree; }
|
||||
|
||||
int BytesTotal() { AUTO_LOCK(m_mutex); return NumTotal() * BLOCK_SIZE; }
|
||||
int BytesAllocated() { AUTO_LOCK(m_mutex); return NumAllocated() * BLOCK_SIZE; }
|
||||
int BytesFree() { AUTO_LOCK(m_mutex); return NumFree() * BLOCK_SIZE; }
|
||||
|
||||
int ItemSize() { return ITEM_SIZE; }
|
||||
int BlockSize() { return BLOCK_SIZE; }
|
||||
int ChunkSize() { return CHUNK_SIZE; }
|
||||
|
||||
private:
|
||||
struct FreeBlock_t
|
||||
{
|
||||
FreeBlock_t* pNext;
|
||||
byte reserved[BLOCK_SIZE - sizeof(FreeBlock_t*)];
|
||||
};
|
||||
|
||||
CUtlVector<void*> m_Chunks; // Chunks are tracked outside blocks (unlike CUtlMemoryPool) to simplify alignment issues
|
||||
FreeBlock_t* m_pFirstFree;
|
||||
int m_nFree;
|
||||
CAllocator m_Allocator;
|
||||
double m_TimeLastCompact;
|
||||
|
||||
CThreadFastMutex m_mutex;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Pool variant using standard allocation
|
||||
// TODO[ AMOS ]: commented from here as CTSList isn't implemented yet;
|
||||
// structure and the size thereof are unknown.
|
||||
//-----------------------------------------------------------------------------
|
||||
//template <typename T, int nInitialCount = 0, bool bDefCreateNewIfEmpty = true >
|
||||
//class CObjectPool
|
||||
//{
|
||||
//public:
|
||||
// CObjectPool()
|
||||
// {
|
||||
// int i = nInitialCount;
|
||||
// while (i-- > 0)
|
||||
// {
|
||||
// m_AvailableObjects.PushItem(new T);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// ~CObjectPool()
|
||||
// {
|
||||
// Purge();
|
||||
// }
|
||||
//
|
||||
// int NumAvailable()
|
||||
// {
|
||||
// return m_AvailableObjects.Count();
|
||||
// }
|
||||
//
|
||||
// void Purge()
|
||||
// {
|
||||
// T* p = NULL;
|
||||
// while (m_AvailableObjects.PopItem(&p))
|
||||
// {
|
||||
// delete p;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// T* GetObject(bool bCreateNewIfEmpty = bDefCreateNewIfEmpty)
|
||||
// {
|
||||
// T* p = NULL;
|
||||
// if (!m_AvailableObjects.PopItem(&p))
|
||||
// {
|
||||
// p = (bCreateNewIfEmpty) ? new T : NULL;
|
||||
// }
|
||||
// return p;
|
||||
// }
|
||||
//
|
||||
// void PutObject(T* p)
|
||||
// {
|
||||
// m_AvailableObjects.PushItem(p);
|
||||
// }
|
||||
//
|
||||
//private:
|
||||
// CTSList<T*> m_AvailableObjects;
|
||||
//};
|
||||
|
||||
////-----------------------------------------------------------------------------
|
||||
//// Fixed budget pool with overflow to malloc
|
||||
////-----------------------------------------------------------------------------
|
||||
//template <size_t PROVIDED_ITEM_SIZE, int ITEM_COUNT>
|
||||
//class CFixedBudgetMemoryPool
|
||||
//{
|
||||
//public:
|
||||
// CFixedBudgetMemoryPool()
|
||||
// {
|
||||
// m_pBase = m_pLimit = 0;
|
||||
// COMPILE_TIME_ASSERT(ITEM_SIZE % 4 == 0);
|
||||
// }
|
||||
//
|
||||
// bool Owns(void* p)
|
||||
// {
|
||||
// return (p >= m_pBase && p < m_pLimit);
|
||||
// }
|
||||
//
|
||||
// void* Alloc()
|
||||
// {
|
||||
// MEM_ALLOC_CREDIT_CLASS();
|
||||
//#ifndef USE_MEM_DEBUG
|
||||
// if (!m_pBase)
|
||||
// {
|
||||
// LOCAL_THREAD_LOCK();
|
||||
// if (!m_pBase)
|
||||
// {
|
||||
// byte* pMemory = m_pBase = (byte*)malloc(ITEM_COUNT * ITEM_SIZE);
|
||||
// m_pLimit = m_pBase + (ITEM_COUNT * ITEM_SIZE);
|
||||
//
|
||||
// for (int i = 0; i < ITEM_COUNT; i++)
|
||||
// {
|
||||
// m_freeList.Push((TSLNodeBase_t*)pMemory);
|
||||
// pMemory += ITEM_SIZE;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// void* p = m_freeList.Pop();
|
||||
// if (p)
|
||||
// return p;
|
||||
//#endif
|
||||
// return malloc(ITEM_SIZE);
|
||||
// }
|
||||
//
|
||||
// void Free(void* p)
|
||||
// {
|
||||
//#ifndef USE_MEM_DEBUG
|
||||
// if (Owns(p))
|
||||
// m_freeList.Push((TSLNodeBase_t*)p);
|
||||
// else
|
||||
//#endif
|
||||
// free(p);
|
||||
// }
|
||||
//
|
||||
// void Clear()
|
||||
// {
|
||||
//#ifndef USE_MEM_DEBUG
|
||||
// if (m_pBase)
|
||||
// {
|
||||
// free(m_pBase);
|
||||
// }
|
||||
// m_pBase = m_pLimit = 0;
|
||||
// Construct(&m_freeList);
|
||||
//#endif
|
||||
// }
|
||||
//
|
||||
// bool IsEmpty()
|
||||
// {
|
||||
//#ifndef USE_MEM_DEBUG
|
||||
// if (m_pBase && m_freeList.Count() != ITEM_COUNT)
|
||||
// return false;
|
||||
//#endif
|
||||
// return true;
|
||||
// }
|
||||
//
|
||||
// enum
|
||||
// {
|
||||
// ITEM_SIZE = ALIGN_VALUE(PROVIDED_ITEM_SIZE, TSLIST_NODE_ALIGNMENT)
|
||||
// };
|
||||
//
|
||||
// CTSListBase m_freeList;
|
||||
// byte* m_pBase;
|
||||
// byte* m_pLimit;
|
||||
//};
|
||||
//
|
||||
//#define BIND_TO_FIXED_BUDGET_POOL( poolName ) \
|
||||
// inline void* operator new( size_t size ) { return poolName.Alloc(); } \
|
||||
// inline void* operator new( size_t size, int nBlockUse, const char *pFileName, int nLine ) { return poolName.Alloc(); } \
|
||||
// inline void operator delete( void* p ) { poolName.Free(p); } \
|
||||
// inline void operator delete( void* p, int nBlockUse, const char *pFileName, int nLine ) { poolName.Free(p); }
|
||||
//
|
||||
////-----------------------------------------------------------------------------
|
||||
//
|
||||
//
|
||||
//template< class T >
|
||||
//inline T* CClassMemoryPool<T>::Alloc()
|
||||
//{
|
||||
// T* pRet;
|
||||
//
|
||||
// {
|
||||
// MEM_ALLOC_CREDIT_CLASS();
|
||||
// pRet = (T*)CUtlMemoryPool::Alloc();
|
||||
// }
|
||||
//
|
||||
// if (pRet)
|
||||
// {
|
||||
// Construct(pRet);
|
||||
// }
|
||||
// return pRet;
|
||||
//}
|
||||
//
|
||||
//template< class T >
|
||||
//inline T* CClassMemoryPool<T>::AllocZero()
|
||||
//{
|
||||
// T* pRet;
|
||||
//
|
||||
// {
|
||||
// MEM_ALLOC_CREDIT_CLASS();
|
||||
// pRet = (T*)CUtlMemoryPool::AllocZero();
|
||||
// }
|
||||
//
|
||||
// if (pRet)
|
||||
// {
|
||||
// Construct(pRet);
|
||||
// }
|
||||
// return pRet;
|
||||
//}
|
||||
//
|
||||
//template< class T >
|
||||
//inline void CClassMemoryPool<T>::Free(T* pMem)
|
||||
//{
|
||||
// if (pMem)
|
||||
// {
|
||||
// Destruct(pMem);
|
||||
// }
|
||||
//
|
||||
// CUtlMemoryPool::Free(pMem);
|
||||
//}
|
||||
//
|
||||
//template< class T >
|
||||
//inline void CClassMemoryPool<T>::Clear()
|
||||
//{
|
||||
// CUtlRBTree<void*, int> freeBlocks;
|
||||
// SetDefLessFunc(freeBlocks);
|
||||
//
|
||||
// void* pCurFree = m_pHeadOfFreeList;
|
||||
// while (pCurFree != NULL)
|
||||
// {
|
||||
// freeBlocks.Insert(pCurFree);
|
||||
// pCurFree = *((void**)pCurFree);
|
||||
// }
|
||||
//
|
||||
// for (CBlob* pCur = m_BlobHead.m_pNext; pCur != &m_BlobHead; pCur = pCur->m_pNext)
|
||||
// {
|
||||
// int nElements = pCur->m_NumBytes / this->m_BlockSize;
|
||||
// T* p = (T*)AlignValue(pCur->m_Data, this->m_nAlignment);
|
||||
// T* pLimit = p + nElements;
|
||||
// while (p < pLimit)
|
||||
// {
|
||||
// if (freeBlocks.Find(p) == freeBlocks.InvalidIndex())
|
||||
// {
|
||||
// Destruct(p);
|
||||
// }
|
||||
// p++;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// CUtlMemoryPool::Clear();
|
||||
//}
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
////-----------------------------------------------------------------------------
|
||||
//// Macros that make it simple to make a class use a fixed-size allocator
|
||||
//// Put DECLARE_FIXEDSIZE_ALLOCATOR in the private section of a class,
|
||||
//// Put DEFINE_FIXEDSIZE_ALLOCATOR in the CPP file
|
||||
////-----------------------------------------------------------------------------
|
||||
//#define DECLARE_FIXEDSIZE_ALLOCATOR( _class ) \
|
||||
// public: \
|
||||
// inline void* operator new( size_t size ) { MEM_ALLOC_CREDIT_(#_class " pool"); return s_Allocator.Alloc(size); } \
|
||||
// inline void* operator new( size_t size, int nBlockUse, const char *pFileName, int nLine ) { MEM_ALLOC_CREDIT_(#_class " pool"); return s_Allocator.Alloc(size); } \
|
||||
// inline void operator delete( void* p ) { s_Allocator.Free(p); } \
|
||||
// inline void operator delete( void* p, int nBlockUse, const char *pFileName, int nLine ) { s_Allocator.Free(p); } \
|
||||
// private: \
|
||||
// static CUtlMemoryPool s_Allocator
|
||||
//
|
||||
//#define DEFINE_FIXEDSIZE_ALLOCATOR( _class, _initsize, _grow ) \
|
||||
// CUtlMemoryPool _class::s_Allocator(sizeof(_class), _initsize, _grow, #_class " pool")
|
||||
//
|
||||
//#define DEFINE_FIXEDSIZE_ALLOCATOR_ALIGNED( _class, _initsize, _grow, _alignment ) \
|
||||
// CUtlMemoryPool _class::s_Allocator(sizeof(_class), _initsize, _grow, #_class " pool", _alignment )
|
||||
//
|
||||
//#define DECLARE_FIXEDSIZE_ALLOCATOR_MT( _class ) \
|
||||
// public: \
|
||||
// inline void* operator new( size_t size ) { MEM_ALLOC_CREDIT_(#_class " pool"); return s_Allocator.Alloc(size); } \
|
||||
// inline void* operator new( size_t size, int nBlockUse, const char *pFileName, int nLine ) { MEM_ALLOC_CREDIT_(#_class " pool"); return s_Allocator.Alloc(size); } \
|
||||
// inline void operator delete( void* p ) { s_Allocator.Free(p); } \
|
||||
// inline void operator delete( void* p, int nBlockUse, const char *pFileName, int nLine ) { s_Allocator.Free(p); } \
|
||||
// private: \
|
||||
// static CMemoryPoolMT s_Allocator
|
||||
//
|
||||
//#define DEFINE_FIXEDSIZE_ALLOCATOR_MT( _class, _initsize, _grow ) \
|
||||
// CMemoryPoolMT _class::s_Allocator(sizeof(_class), _initsize, _grow, #_class " pool")
|
||||
//
|
||||
////-----------------------------------------------------------------------------
|
||||
//// Macros that make it simple to make a class use a fixed-size allocator
|
||||
//// This version allows us to use a memory pool which is externally defined...
|
||||
//// Put DECLARE_FIXEDSIZE_ALLOCATOR_EXTERNAL in the private section of a class,
|
||||
//// Put DEFINE_FIXEDSIZE_ALLOCATOR_EXTERNAL in the CPP file
|
||||
////-----------------------------------------------------------------------------
|
||||
//
|
||||
//#define DECLARE_FIXEDSIZE_ALLOCATOR_EXTERNAL( _class ) \
|
||||
// public: \
|
||||
// inline void* operator new( size_t size ) { MEM_ALLOC_CREDIT_(#_class " pool"); return s_pAllocator->Alloc(size); } \
|
||||
// inline void* operator new( size_t size, int nBlockUse, const char *pFileName, int nLine ) { MEM_ALLOC_CREDIT_(#_class " pool"); return s_pAllocator->Alloc(size); } \
|
||||
// inline void operator delete( void* p ) { s_pAllocator->Free(p); } \
|
||||
// private: \
|
||||
// static CUtlMemoryPool* s_pAllocator
|
||||
//
|
||||
//#define DEFINE_FIXEDSIZE_ALLOCATOR_EXTERNAL( _class, _allocator ) \
|
||||
// CUtlMemoryPool* _class::s_pAllocator = _allocator
|
||||
//
|
||||
//
|
||||
//template <int ITEM_SIZE, int ALIGNMENT, int CHUNK_SIZE, class CAllocator, bool GROWMODE, int COMPACT_THRESHOLD >
|
||||
//inline CAlignedMemPool<ITEM_SIZE, ALIGNMENT, CHUNK_SIZE, CAllocator, GROWMODE, COMPACT_THRESHOLD>::CAlignedMemPool()
|
||||
// : m_pFirstFree(0),
|
||||
// m_nFree(0),
|
||||
// m_TimeLastCompact(0)
|
||||
//{
|
||||
// // These COMPILE_TIME_ASSERT checks need to be in individual scopes to avoid build breaks
|
||||
// // on MacOS and Linux due to a gcc bug.
|
||||
// { COMPILE_TIME_ASSERT(sizeof(FreeBlock_t) >= BLOCK_SIZE); }
|
||||
// { COMPILE_TIME_ASSERT(ALIGN_VALUE(sizeof(FreeBlock_t), ALIGNMENT) == sizeof(FreeBlock_t)); }
|
||||
//}
|
||||
//
|
||||
//template <int ITEM_SIZE, int ALIGNMENT, int CHUNK_SIZE, class CAllocator, bool GROWMODE, int COMPACT_THRESHOLD >
|
||||
//inline void* CAlignedMemPool<ITEM_SIZE, ALIGNMENT, CHUNK_SIZE, CAllocator, GROWMODE, COMPACT_THRESHOLD>::Alloc()
|
||||
//{
|
||||
// AUTO_LOCK(m_mutex);
|
||||
//
|
||||
// if (!m_pFirstFree)
|
||||
// {
|
||||
// if (!GROWMODE && m_Chunks.Count())
|
||||
// {
|
||||
// return NULL;
|
||||
// }
|
||||
//
|
||||
// FreeBlock_t* pNew = (FreeBlock_t*)m_Allocator.Alloc(CHUNK_SIZE);
|
||||
// Assert((unsigned)pNew % ALIGNMENT == 0);
|
||||
// m_Chunks.AddToTail(pNew);
|
||||
// m_nFree = CHUNK_SIZE / BLOCK_SIZE;
|
||||
// m_pFirstFree = pNew;
|
||||
// for (int i = 0; i < m_nFree - 1; i++)
|
||||
// {
|
||||
// pNew->pNext = pNew + 1;
|
||||
// pNew++;
|
||||
// }
|
||||
// pNew->pNext = NULL;
|
||||
// }
|
||||
//
|
||||
// void* p = m_pFirstFree;
|
||||
// m_pFirstFree = m_pFirstFree->pNext;
|
||||
// m_nFree--;
|
||||
//
|
||||
// return p;
|
||||
//}
|
||||
//
|
||||
//template <int ITEM_SIZE, int ALIGNMENT, int CHUNK_SIZE, class CAllocator, bool GROWMODE, int COMPACT_THRESHOLD >
|
||||
//inline void CAlignedMemPool<ITEM_SIZE, ALIGNMENT, CHUNK_SIZE, CAllocator, GROWMODE, COMPACT_THRESHOLD>::Free(void* p)
|
||||
//{
|
||||
// AUTO_LOCK(m_mutex);
|
||||
//
|
||||
// // Insertion sort to encourage allocation clusters in chunks
|
||||
// FreeBlock_t* pFree = ((FreeBlock_t*)p);
|
||||
// FreeBlock_t* pCur = m_pFirstFree;
|
||||
// FreeBlock_t* pPrev = NULL;
|
||||
//
|
||||
// while (pCur && pFree > pCur)
|
||||
// {
|
||||
// pPrev = pCur;
|
||||
// pCur = pCur->pNext;
|
||||
// }
|
||||
//
|
||||
// pFree->pNext = pCur;
|
||||
//
|
||||
// if (pPrev)
|
||||
// {
|
||||
// pPrev->pNext = pFree;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// m_pFirstFree = pFree;
|
||||
// }
|
||||
// m_nFree++;
|
||||
//
|
||||
// if (m_nFree >= (CHUNK_SIZE / BLOCK_SIZE) * COMPACT_THRESHOLD)
|
||||
// {
|
||||
// double time = Plat_FloatTime();
|
||||
// double compactTime = (m_nFree >= (CHUNK_SIZE / BLOCK_SIZE) * COMPACT_THRESHOLD * 4) ? 15.0 : 30.0;
|
||||
// if (m_TimeLastCompact > time || m_TimeLastCompact + compactTime < time)
|
||||
// {
|
||||
// Compact();
|
||||
// m_TimeLastCompact = time;
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//template <int ITEM_SIZE, int ALIGNMENT, int CHUNK_SIZE, class CAllocator, bool GROWMODE, int COMPACT_THRESHOLD >
|
||||
//inline int __cdecl CAlignedMemPool<ITEM_SIZE, ALIGNMENT, CHUNK_SIZE, CAllocator, GROWMODE, COMPACT_THRESHOLD>::CompareChunk(void* const* ppLeft, void* const* ppRight)
|
||||
//{
|
||||
// return size_cast<int>((intp)*ppLeft - (intp)*ppRight);
|
||||
//}
|
||||
//
|
||||
//template <int ITEM_SIZE, int ALIGNMENT, int CHUNK_SIZE, class CAllocator, bool GROWMODE, int COMPACT_THRESHOLD >
|
||||
//inline void CAlignedMemPool<ITEM_SIZE, ALIGNMENT, CHUNK_SIZE, CAllocator, GROWMODE, COMPACT_THRESHOLD>::Compact()
|
||||
//{
|
||||
// FreeBlock_t* pCur = m_pFirstFree;
|
||||
// FreeBlock_t* pPrev = NULL;
|
||||
//
|
||||
// m_Chunks.Sort(CompareChunk);
|
||||
//
|
||||
//#ifdef VALIDATE_ALIGNED_MEM_POOL
|
||||
// {
|
||||
// FreeBlock_t* p = m_pFirstFree;
|
||||
// while (p)
|
||||
// {
|
||||
// if (p->pNext && p > p->pNext)
|
||||
// {
|
||||
// __asm { int 3 }
|
||||
// }
|
||||
// p = p->pNext;
|
||||
// }
|
||||
//
|
||||
// for (int i = 0; i < m_Chunks.Count(); i++)
|
||||
// {
|
||||
// if (i + 1 < m_Chunks.Count())
|
||||
// {
|
||||
// if (m_Chunks[i] > m_Chunks[i + 1])
|
||||
// {
|
||||
// __asm { int 3 }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//#endif
|
||||
//
|
||||
// int i;
|
||||
//
|
||||
// for (i = 0; i < m_Chunks.Count(); i++)
|
||||
// {
|
||||
// int nBlocksPerChunk = CHUNK_SIZE / BLOCK_SIZE;
|
||||
// FreeBlock_t* pChunkLimit = ((FreeBlock_t*)m_Chunks[i]) + nBlocksPerChunk;
|
||||
// int nFromChunk = 0;
|
||||
// if (pCur == m_Chunks[i])
|
||||
// {
|
||||
// FreeBlock_t* pFirst = pCur;
|
||||
// while (pCur && pCur >= m_Chunks[i] && pCur < pChunkLimit)
|
||||
// {
|
||||
// pCur = pCur->pNext;
|
||||
// nFromChunk++;
|
||||
// }
|
||||
// pCur = pFirst;
|
||||
//
|
||||
// }
|
||||
//
|
||||
// while (pCur && pCur >= m_Chunks[i] && pCur < pChunkLimit)
|
||||
// {
|
||||
// if (nFromChunk != nBlocksPerChunk)
|
||||
// {
|
||||
// if (pPrev)
|
||||
// {
|
||||
// pPrev->pNext = pCur;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// m_pFirstFree = pCur;
|
||||
// }
|
||||
// pPrev = pCur;
|
||||
// }
|
||||
// else if (pPrev)
|
||||
// {
|
||||
// pPrev->pNext = NULL;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// m_pFirstFree = NULL;
|
||||
// }
|
||||
//
|
||||
// pCur = pCur->pNext;
|
||||
// }
|
||||
//
|
||||
// if (nFromChunk == nBlocksPerChunk)
|
||||
// {
|
||||
// m_Allocator.Free(m_Chunks[i]);
|
||||
// m_nFree -= nBlocksPerChunk;
|
||||
// m_Chunks[i] = 0;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// for (i = m_Chunks.Count() - 1; i >= 0; i--)
|
||||
// {
|
||||
// if (!m_Chunks[i])
|
||||
// {
|
||||
// m_Chunks.FastRemove(i);
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
#endif // MEMPOOL_H
|
||||
|
@ -18,6 +18,7 @@ add_sources( SOURCE_GROUP "Utility"
|
||||
"utlbuffer.cpp"
|
||||
"utlstring.cpp"
|
||||
"characterset.cpp"
|
||||
"mempool.cpp"
|
||||
#"memstack.cpp"
|
||||
)
|
||||
|
||||
|
347
r5dev/tier1/mempool.cpp
Normal file
347
r5dev/tier1/mempool.cpp
Normal file
@ -0,0 +1,347 @@
|
||||
//===== Copyright (c) 1996-2005, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#include "tier1/mempool.h"
|
||||
#include <stdio.h>
|
||||
#include <memory.h>
|
||||
#include "tier0/dbg.h"
|
||||
#include <ctype.h>
|
||||
#include "tier1/strtools.h"
|
||||
|
||||
#ifndef _PS3
|
||||
#include <malloc.h>
|
||||
#endif
|
||||
|
||||
// Should be last include
|
||||
#include "tier0/memdbgon.h"
|
||||
#include <public/tier0/imemalloc.h>
|
||||
|
||||
MemoryPoolReportFunc_t CUtlMemoryPool::g_ReportFunc = 0;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Error reporting... (debug only)
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void CUtlMemoryPool::SetErrorReportFunc(MemoryPoolReportFunc_t func)
|
||||
{
|
||||
g_ReportFunc = func;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Constructor
|
||||
//-----------------------------------------------------------------------------
|
||||
CUtlMemoryPool::CUtlMemoryPool(int blockSize, int numElements, int growMode, const char* pszAllocOwner, unsigned short nAlignment)
|
||||
{
|
||||
#ifdef _X360
|
||||
if (numElements > 0 && growMode != GROW_NONE)
|
||||
{
|
||||
numElements = 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
m_nAlignment = (nAlignment != 0) ? nAlignment : 1;
|
||||
Assert(IsPowerOfTwo(m_nAlignment));
|
||||
m_BlockSize = blockSize < sizeof(void*) ? sizeof(void*) : blockSize;
|
||||
m_BlockSize = AlignValue(m_BlockSize, m_nAlignment);
|
||||
m_BlocksPerBlob = numElements;
|
||||
m_PeakAlloc = 0;
|
||||
m_GrowMode = growMode;
|
||||
|
||||
if (!pszAllocOwner)
|
||||
{
|
||||
pszAllocOwner = __FILE__;
|
||||
}
|
||||
|
||||
m_pszAllocOwner = pszAllocOwner;
|
||||
|
||||
Init();
|
||||
AddNewBlob();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Frees the memory contained in the mempool, and invalidates it for
|
||||
// any further use.
|
||||
// Input : *memPool - the mempool to shutdown
|
||||
//-----------------------------------------------------------------------------
|
||||
CUtlMemoryPool::~CUtlMemoryPool()
|
||||
{
|
||||
if (m_BlocksAllocated > 0)
|
||||
{
|
||||
ReportLeaks();
|
||||
}
|
||||
Clear();
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Resets the pool
|
||||
//-----------------------------------------------------------------------------
|
||||
void CUtlMemoryPool::Init()
|
||||
{
|
||||
m_NumBlobs = 0;
|
||||
m_BlocksAllocated = 0;
|
||||
m_pNext = m_pPrev = NULL;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Frees everything
|
||||
//-----------------------------------------------------------------------------
|
||||
void CUtlMemoryPool::Clear()
|
||||
{
|
||||
// Free everything..
|
||||
|
||||
CBlob* pPrev;
|
||||
for (CBlob* pCur = m_pNext; pCur; pCur = pPrev)
|
||||
{
|
||||
pPrev = pCur->m_pPrev;
|
||||
free(pCur);
|
||||
}
|
||||
Init();
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Is an allocation within the pool?
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CUtlMemoryPool::IsAllocationWithinPool(void* pMem) const
|
||||
{
|
||||
for (CBlob* pCur = m_pNext; pCur; pCur = pCur->m_pPrev)
|
||||
{
|
||||
// Is the allocation within the blob?
|
||||
if ((pMem < pCur->m_Data) || (pMem >= pCur->m_Data + pCur->m_NumBytes))
|
||||
continue;
|
||||
|
||||
// Make sure the allocation is on a block boundary
|
||||
intp pFirstAllocation = AlignValue((intp)pCur->m_Data, m_nAlignment);
|
||||
|
||||
intp nOffset = (intp)pMem - pFirstAllocation;
|
||||
return (nOffset % m_BlockSize) == 0;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Reports memory leaks
|
||||
//-----------------------------------------------------------------------------
|
||||
void CUtlMemoryPool::ReportLeaks()
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
if (!g_ReportFunc)
|
||||
return;
|
||||
|
||||
g_ReportFunc("Memory leak: mempool blocks left in memory: %d\n", m_BlocksAllocated);
|
||||
|
||||
// walk and destroy the free list so it doesn't intefere in the scan
|
||||
while (m_pPrev != NULL)
|
||||
{
|
||||
void* next = *((void**)m_pPrev);
|
||||
memset(m_pPrev, 0, m_BlockSize);
|
||||
m_pPrev = (CBlob*)next;
|
||||
}
|
||||
|
||||
g_ReportFunc("Dumping memory: \'");
|
||||
|
||||
for (CBlob* pCur = m_pNext; pCur; pCur = pCur->m_pPrev)
|
||||
{
|
||||
// scan the memory block and dump the leaks
|
||||
char* scanPoint = (char*)pCur->m_Data;
|
||||
char* scanEnd = pCur->m_Data + pCur->m_NumBytes;
|
||||
bool needSpace = false;
|
||||
|
||||
while (scanPoint < scanEnd)
|
||||
{
|
||||
// search for and dump any strings
|
||||
if ((unsigned)(*scanPoint + 1) <= 256 && isprint(*scanPoint))
|
||||
{
|
||||
g_ReportFunc("%c", *scanPoint);
|
||||
needSpace = true;
|
||||
}
|
||||
else if (needSpace)
|
||||
{
|
||||
needSpace = false;
|
||||
g_ReportFunc(" ");
|
||||
}
|
||||
|
||||
scanPoint++;
|
||||
}
|
||||
}
|
||||
|
||||
g_ReportFunc("\'\n");
|
||||
#endif // _DEBUG
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CUtlMemoryPool::AddNewBlob()
|
||||
{
|
||||
MEM_ALLOC_CREDIT_(m_pszAllocOwner);
|
||||
|
||||
int sizeMultiplier;
|
||||
|
||||
if (m_GrowMode == GROW_SLOW)
|
||||
{
|
||||
sizeMultiplier = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_GrowMode == GROW_NONE)
|
||||
{
|
||||
// Can only have one allocation when we're in this mode
|
||||
if (m_NumBlobs != 0)
|
||||
{
|
||||
Assert(!"CUtlMemoryPool::AddNewBlob: mode == GROW_NONE");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// GROW_FAST and GROW_NONE use this.
|
||||
sizeMultiplier = m_NumBlobs + 1;
|
||||
}
|
||||
|
||||
// maybe use something other than malloc?
|
||||
int nElements = m_BlocksPerBlob * sizeMultiplier;
|
||||
int blobSize = m_BlockSize * nElements;
|
||||
CBlob* pBlob = (CBlob*)malloc(sizeof(CBlob) - 1 + blobSize + (m_nAlignment - 1));
|
||||
Assert(pBlob);
|
||||
|
||||
// Link it in at the end of the blob list.
|
||||
pBlob->m_NumBytes = blobSize;
|
||||
pBlob->m_pNext = m_pNext;
|
||||
|
||||
m_pNext = pBlob;
|
||||
m_pPrev = (CBlob*)AlignValue(pBlob->m_Data, m_nAlignment);
|
||||
|
||||
Assert(m_pPrev);
|
||||
|
||||
void** newBlob = (void**)m_pPrev;
|
||||
for (int j = 0; j < nElements - 1; j++)
|
||||
{
|
||||
newBlob[0] = (char*)newBlob + m_BlockSize;
|
||||
newBlob = (void**)newBlob[0];
|
||||
}
|
||||
|
||||
// null terminate list
|
||||
newBlob[0] = NULL;
|
||||
m_NumBlobs++;
|
||||
}
|
||||
|
||||
|
||||
void* CUtlMemoryPool::Alloc()
|
||||
{
|
||||
return Alloc(m_BlockSize);
|
||||
}
|
||||
|
||||
|
||||
void* CUtlMemoryPool::AllocZero()
|
||||
{
|
||||
return AllocZero(m_BlockSize);
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Allocs a single block of memory from the pool.
|
||||
// Input : amount -
|
||||
//-----------------------------------------------------------------------------
|
||||
void* CUtlMemoryPool::Alloc(size_t amount)
|
||||
{
|
||||
if (amount > (size_t)m_BlockSize)
|
||||
return NULL;
|
||||
|
||||
if (!m_pPrev)
|
||||
{
|
||||
// returning NULL is fine in GROW_NONE
|
||||
if (m_GrowMode == GROW_NONE && m_NumBlobs > 0)
|
||||
{
|
||||
Assert( !"CUtlMemoryPool::Alloc: tried to make new blob with GROW_NONE" );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// overflow
|
||||
AddNewBlob();
|
||||
|
||||
// still failure, error out
|
||||
if (!m_pPrev)
|
||||
{
|
||||
Assert(!"CUtlMemoryPool::Alloc: ran out of memory");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
m_BlocksAllocated++;
|
||||
m_PeakAlloc = MAX(m_PeakAlloc, m_BlocksAllocated);
|
||||
|
||||
void* returnBlock = m_pPrev;
|
||||
|
||||
// move the pointer the next block
|
||||
m_pPrev = m_pPrev->m_pPrev;
|
||||
|
||||
return returnBlock;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Allocs a single block of memory from the pool, zeroes the memory before returning
|
||||
// Input : amount -
|
||||
//-----------------------------------------------------------------------------
|
||||
void* CUtlMemoryPool::AllocZero(size_t amount)
|
||||
{
|
||||
void* mem = Alloc(amount);
|
||||
if (mem)
|
||||
{
|
||||
memset(mem, 0x00, (int)amount);
|
||||
}
|
||||
return mem;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Frees a block of memory
|
||||
// Input : *memBlock - the memory to free
|
||||
//-----------------------------------------------------------------------------
|
||||
void CUtlMemoryPool::Free(void* memBlock)
|
||||
{
|
||||
if (!memBlock)
|
||||
return; // trying to delete NULL pointer, ignore
|
||||
|
||||
#ifdef _DEBUG
|
||||
// check to see if the memory is from the allocated range
|
||||
bool bOK = false;
|
||||
for (CBlob* pCur = m_pNext; pCur; pCur = pCur->m_pPrev)
|
||||
{
|
||||
if (memBlock >= pCur->m_Data && (char*)memBlock < (pCur->m_Data + pCur->m_NumBytes))
|
||||
{
|
||||
bOK = true;
|
||||
}
|
||||
}
|
||||
Assert(bOK);
|
||||
#endif // _DEBUG
|
||||
|
||||
#ifdef _DEBUG
|
||||
// invalidate the memory
|
||||
memset(memBlock, 0xDD, m_BlockSize);
|
||||
#endif
|
||||
|
||||
m_BlocksAllocated--;
|
||||
|
||||
// make the block point to the first item in the list
|
||||
*((void**)memBlock) = m_pPrev;
|
||||
|
||||
// the list head is now the new block
|
||||
m_pPrev = (CBlob*)memBlock;
|
||||
}
|
||||
|
||||
int CUtlMemoryPool::Size() const
|
||||
{
|
||||
uint32 size = 0;
|
||||
|
||||
for (CBlob* pCur = m_pNext; pCur; pCur = pCur->m_pPrev)
|
||||
{
|
||||
size += pCur->m_NumBytes;
|
||||
}
|
||||
return size;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user