Files
watrbx-game-engine/Base/include/rbx/Memory.h
T
2025-09-18 17:55:52 -04:00

276 lines
7.0 KiB
C++

#ifndef _0632EE02291848e49902EAA033B8C2EA
#define _0632EE02291848e49902EAA033B8C2EA
#include "boost/pool/singleton_pool.hpp"
#include "boost/scoped_ptr.hpp"
#include <assert.h>
#include "rbx/debug.h"
#include "rbx/atomic.h"
#include <vector>
// Note - Interlock Incs, Decs are turned off on count because of possible performance issues
// TODO: Benchmark RBX_ALLOCATOR_COUNTS
#ifdef _DEBUG
#define RBX_ALLOCATOR_COUNTS
#define RBX_POOL_ALLOCATION_STATS
#endif
// TODO: Benchmark:
#ifndef _DEBUG
// Note: Using this option makes it harder to find memory leaks
#define RBX_ALLOCATOR_SINGLETON_POOL
#endif
// TODO: Benchmark:
//#define RBX_MEMORY_SCALABLE_MALLOC
namespace RBX {
#ifdef RBX_POOL_ALLOCATION_STATS
extern std::vector<size_t*> poolAllocationList;
#endif
typedef bool (*releaseFunc)();
extern std::vector<size_t*> poolAvailabilityList;
extern std::vector<releaseFunc> poolReleaseMemoryFuncList;
inline void addToPool(size_t* allocatedSize, size_t* availableSize, size_t size)
{
if (size > *availableSize)
{
#ifdef RBX_POOL_ALLOCATION_STATS
(*allocatedSize)+=(size);
#endif
}
else
{
(*availableSize)-=(size);
}
}
inline void removeFromPool(size_t* availableSize, size_t size)
{
(*availableSize)+=(size);
}
// You can use this allocator when using std or boost collections
class roblox_allocator
{
public:
static bool crashOnAllocationFailure; // TODO: Put this in more places, including std allocator overrides?
typedef std::size_t size_type;
typedef std::ptrdiff_t difference_type;
static char* malloc(const size_type bytes);
static void free(char* const block);
static char* realloc(char* ptr, size_t nsize);
};
template<class T>
class Allocator
{
#ifdef RBX_ALLOCATOR_COUNTS
static rbx::atomic<int> count;
#endif
public:
static size_t allocatedSize;
static size_t availableSize;
static bool initialized;
Allocator()
{
if (!initialized)
{
#ifdef RBX_POOL_ALLOCATION_STATS
poolAllocationList.push_back(&allocatedSize);
#endif
poolAvailabilityList.push_back(&availableSize);
bool (*pReleaseMemory)() = releaseMemory;
poolReleaseMemoryFuncList.push_back(pReleaseMemory);
initialized = true;
}
}
#ifdef RBX_ALLOCATOR_SINGLETON_POOL
// TODO: Benchmark this allocator vs. other kinds
void* operator new(size_t nSize) {
assert(nSize==sizeof(T));
void* result = boost::singleton_pool<T, sizeof(T), boost::default_user_allocator_malloc_free>::malloc();
if (!result)
{
if (roblox_allocator::crashOnAllocationFailure)
RBXCRASH(); // We want a nice fat crash here so that the process quits and we can log it
throw std::bad_alloc();
}
#ifdef RBX_ALLOCATOR_COUNTS
count++;
#endif
addToPool(&allocatedSize, &availableSize, nSize);
return result;
}
void* operator new( size_t size, void* p )
{
addToPool(&allocatedSize, &availableSize, size);
return p;
}
void operator delete(void*, void*)
{
removeFromPool(&availableSize, sizeof(T));
}
static bool releaseMemory()
{
#ifdef RBX_POOL_ALLOCATION_STATS
allocatedSize -= availableSize;
#endif
availableSize = 0;
return boost::singleton_pool<T, sizeof(T), boost::default_user_allocator_malloc_free>::release_memory();
}
static bool purgeMemory()
{
// Be very careful when calling this as this is singleton pool purge
#ifdef RBX_POOL_ALLOCATION_STATS
allocatedSize = 0;
#endif
availableSize = 0;
return boost::singleton_pool<T, sizeof(T), boost::default_user_allocator_malloc_free>::purge_memory();
}
void operator delete(void* p) {
boost::singleton_pool<T, sizeof(T), boost::default_user_allocator_malloc_free>::free(p);
#ifdef RBX_ALLOCATOR_COUNTS
count--;
#endif
removeFromPool(&availableSize, sizeof(T));
}
/////////////////////////////////////////////////////////////////////////////////////
#else
void* operator new(size_t nSize) {
assert(nSize==sizeof(T));
void* result = (void*)roblox_allocator::malloc(nSize);
if (!result)
{
if (roblox_allocator::crashOnAllocationFailure)
RBXCRASH(); // We want a nice fat crash here so that the process quits and we can log it
throw std::bad_alloc();
}
#ifdef RBX_ALLOCATOR_COUNTS
count++;
#endif
return result;
}
void operator delete(void* p) {
roblox_allocator::free((char*)p);
#ifdef RBX_ALLOCATOR_COUNTS
count--;
#endif
}
void* operator new( size_t size, void* p )
{
return p;
}
void operator delete(void*, void*)
{
// placement delete, nothing to do
}
static bool releaseMemory()
{
// pool not used, nothing to do
return true;
}
static bool purgeMemory()
{
// pool not used, nothing to do
return true;
}
#endif
//////////////////////////////////////////////////////////////////////////////////////////
#ifdef RBX_ALLOCATOR_COUNTS
static long getCount() {return count; }
static long getHeapSize() {return sizeof(T) * count; }
#endif
};
template<class T>
size_t Allocator<T>::allocatedSize = 0;
template<class T>
size_t Allocator<T>::availableSize = 0;
template<class T>
bool Allocator<T>::initialized = false;
#ifdef RBX_ALLOCATOR_COUNTS
template<class T>
rbx::atomic<int> Allocator<T>::count;
#endif
// This class is a wrapper for boost::pool<>. It allocates extra memory used by AutoPoolObject
// to store a pointer back to the pool.
class AutoMemPool
{
boost::scoped_ptr< boost::pool<> > pool;
public:
// A pool object that auto free itself from the pool it was allocated from
// MUST use this with AutoMemPool
class Object
{
public:
void* operator new(size_t size, AutoMemPool* pool)
{
RBXASSERT(((size_t)pool->getRequestedSize()) == size + sizeof(AutoMemPool*));
void* mem = pool->malloc();
*(AutoMemPool**)mem = &(*pool); // store the pool at start of memory block
return (char*)mem + sizeof(AutoMemPool*); // skip over the pool
}
void operator delete(void* p, AutoMemPool* pool)
{
pool->free(p);
}
void operator delete(void *p)
{
p = (char*)p - sizeof(AutoMemPool*);
AutoMemPool* pool = *(AutoMemPool**)p;
pool->free(p);
}
};
AutoMemPool(int requested_size)
{
// allocate extra bytes to store pointer to the pool
pool.reset(new boost::pool<>(requested_size + sizeof(this)));
}
inline void* malloc()
{
return pool->malloc();
}
inline void free(void* p)
{
RBXASSERT(pool->is_from(p));
pool->free(p);
}
inline int getRequestedSize()
{
return int(pool->get_requested_size());
}
};
}
#endif