Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
184 changes: 173 additions & 11 deletions source/qcommon/mem.c
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,10 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "mod_mem.h"
#include "mem.h"

#define MAX_SCOPE_NAME 32
#define POOLNAMESIZE 128
#define MEMALIGNMENT_DEFAULT 16
#define MIN_MEM_ALIGNMENT sizeof(void*)

#define AllocHashSize ( 1u << 12u )

static const unsigned int prefixPattern = 0xbaadf00d; // Fill pattern for bytes preceeding allocated blocks
Expand All @@ -71,6 +71,18 @@ static const unsigned int releasedPattern = 0xdeadbeef; // Fill pattern for d
static bool memory_initialized = false;
static bool commands_initialized = false;

struct memscope_s {
char name[MAX_SCOPE_NAME];
struct memscope_s *parent;
struct memscope_s *child;

struct memscope_s *next;

size_t size;
size_t reserved;
struct memscope_alloc_s *alloc;
qmutex_t* lock;
};

mempool_t* Q_ParentPool() {
return NULL;
Expand Down Expand Up @@ -153,9 +165,9 @@ struct mempool_s

};

cvar_t *developerMemory;


cvar_t *developerMemory;
// used for temporary memory allocations around the engine, not for longterm
// storage, if anything in this pool stays allocated during gameplay, it is
// considered a leak
Expand All @@ -166,6 +178,9 @@ static mempool_t *rootChain = NULL; // root chain of mem pool without parents

static qmutex_t *memMutex;

// parenting and unparenting should be a thread safe operation
static qmutex_t *scopeMutex;

static struct memheader_s *hashTable[AllocHashSize];
static struct memheader_s *reservoirHeaders;
static struct memheader_s **reservoirAllocHeaderBuffer;
Expand Down Expand Up @@ -505,12 +520,12 @@ void *__Q_MallocAligned(size_t align, size_t size, const char* sourceFilename, c
mem->size = size;
mem->realsize = realsize;
if(sourceFilename) {
strncpy(mem->sourceFile, sourceFilename, sizeof(mem->sourceFile));
strncpy(mem->sourceFile, sourceFilename, sizeof(mem->sourceFile) - 1);
} else {
strcpy(mem->sourceFile, "??");
}
if(functionName) {
strncpy(mem->sourceFunc, functionName, sizeof(mem->sourceFunc));
strncpy(mem->sourceFunc, functionName, sizeof(mem->sourceFunc) - 1);
} else {
strcpy(mem->sourceFunc, "??");
}
Expand All @@ -526,7 +541,8 @@ mempool_t *Q_CreatePool( mempool_t *parent, const char *name )
mempool_t *pool = (mempool_t *)malloc( sizeof( mempool_t ) );
if( pool == NULL )
_Mem_Error( "Mem_AllocPool: out of memory" );


QMutex_Lock( memMutex );
memset( pool, 0, sizeof( mempool_t ) );
pool->chain = NULL;
pool->parent = parent;
Expand All @@ -542,7 +558,8 @@ mempool_t *Q_CreatePool( mempool_t *parent, const char *name )
pool->next = rootChain;
rootChain = pool;
}

QMutex_Unlock(memMutex);

return pool;
}

Expand Down Expand Up @@ -597,12 +614,12 @@ void *__Q_Realloc( void *ptr, size_t size, const char *sourceFilename, const cha
mem->baseAddress = baseAddress;
mem->sourceLine = sourceLine;
if(sourceFilename) {
strncpy(mem->sourceFile, sourceFilename, sizeof(mem->sourceFile));
strncpy(mem->sourceFile, sourceFilename, sizeof(mem->sourceFile) - 1);
} else {
strcpy(mem->sourceFile, "??");
}
if(functionName) {
strncpy(mem->sourceFunc, functionName, sizeof(mem->sourceFunc));
strncpy(mem->sourceFunc, functionName, sizeof(mem->sourceFunc) - 1);
} else {
strcpy(mem->sourceFunc, "??");
}
Expand Down Expand Up @@ -785,7 +802,7 @@ void *_Mem_AllocExt( mempool_t *pool, size_t size, size_t alignment, int z, int
mem->realsize = realsize;
mem->sourceLine = fileline;
if(filename) {
strncpy(mem->sourceFile, filename, sizeof(mem->sourceFile));
strncpy(mem->sourceFile, filename, sizeof(mem->sourceFile) - 1);
} else {
strcpy(mem->sourceFile, "??");
}
Expand Down Expand Up @@ -910,7 +927,7 @@ mempool_t *_Mem_AllocPool( mempool_t *parent, const char *name, int flags, const
pool->child = NULL;
pool->totalsize = 0;
pool->realsize = sizeof( mempool_t );
Q_strncpyz( pool->name, name, sizeof( pool->name ) );
Q_strncpyz( pool->name, name, sizeof( pool->name ));

if( parent )
{
Expand Down Expand Up @@ -938,7 +955,6 @@ mempool_t *_Mem_AllocTempPool( const char *name, const char *filename, int filel

void _Mem_FreePool( mempool_t **pool, int musthave, int canthave, const char *filename, int fileline )
{
mempool_t **chainAddress;
#ifdef SHOW_NONFREED
memheader_t *mem;
#endif
Expand Down Expand Up @@ -978,6 +994,151 @@ void Mem_ValidationAllAllocations() {
QMutex_Unlock( memMutex );
}

memscope_t *Q_CreateScope( memscope_t *parent, const char *name )
{
memscope_t *scope = Q_Malloc( sizeof(struct memscope_s) );
if(!scope)
return NULL;
memset(scope, 0, sizeof(struct memscope_s));
if(name) {
strncpy(scope->name, name, sizeof(parent->name) - 1);
} else {
strncpy(scope->name, "??", sizeof(parent->name) - 1);
}
scope->lock = QMutex_Create();
if(parent) {
scope->parent = parent;
QMutex_Lock( scopeMutex );
scope->next = parent->child;
parent->child = scope;
QMutex_Unlock( scopeMutex );
}
return scope;
}

void Q_ScopeAttach( memscope_t *scope, void *ptr )
{
Q_ScopeAttachWithFreeHandle( scope, Q_Free, ptr );
}

void Q_ScopeAttachWithFreeHandle( memscope_t *scope, free_hander_t handle, void *ptr )
{
assert( scope );
if( !ptr ) {
return;
}

QMutex_Lock(scope->lock);
for( size_t i = 0; i < scope->size; i++ ) {
if( scope->alloc[i].ptr == ptr ) {
// we're already tracking this allocation
QMutex_Unlock(scope->lock);
return;
}
}

// need to allocate more to the scope
if( ( scope->size + 1 ) > scope->reserved ) {
scope->reserved = ( scope->reserved == 0 ) ? 16 : ( ( scope->reserved >> 1 ) + scope->reserved ); // grow tracked allocations by 1.5
scope->alloc = Q_Realloc( scope->alloc, scope->reserved * sizeof( struct memscope_s ) );
}

struct memscope_alloc_s *alloc = &scope->alloc[scope->size++];
alloc->ptr = ptr;
alloc->freeHandle = handle;
QMutex_Unlock(scope->lock);
}

static bool __findMemAllocScope( memscope_t *scope, void *ptr, size_t *index )
{
assert( scope );
if( ptr == NULL )
return false;
for( size_t i = 0; i < scope->size; i++ ) {
struct memscope_alloc_s *alloc = &scope->alloc[i];
if( alloc->ptr == ptr ) {
if( index ) {
*index = i;
}
return true;
}
}
return false;
}

bool Q_ScopeHas( memscope_t *scope, void *ptr )
{
return __findMemAllocScope( scope, ptr, NULL );
}

void Q_ScopeRelease( memscope_t *scope, void *ptr )
{
assert( scope );
if( ptr == NULL )
return;
QMutex_Lock(scope->lock);
size_t index;
if( __findMemAllocScope( scope, ptr, &index ) ) {
const size_t bufSize = ( scope->size - ( index + 1 ) ) * sizeof( struct memscope_alloc_s );
if( bufSize > 0 )
memmove( &scope->alloc[index], &scope->alloc[index + 1], bufSize );
scope->size--;
}
QMutex_Unlock(scope->lock);
}

void Q_ScopeFree( memscope_t *scope, void *ptr )
{
assert( scope );
if( ptr == NULL )
return;

QMutex_Lock(scope->lock);
size_t index;
if( __findMemAllocScope( scope, ptr, &index ) ) {
struct memscope_alloc_s *alloc = &scope->alloc[index];
alloc->freeHandle( alloc->ptr );
const size_t bufSize = ( scope->size - ( index + 1 ) ) * sizeof( struct memscope_alloc_s );
if( bufSize > 0 )
memmove( &scope->alloc[index], &scope->alloc[index + 1], bufSize );
scope->size--;
}
QMutex_Unlock(scope->lock);
}

static void __Q_FreeScopeRecuse(memscope_t *scope) {
for( size_t i = 0; i < scope->size; i++ ) {
struct memscope_alloc_s *alloc = &scope->alloc[i];
alloc->freeHandle( alloc->ptr );
}

for( memscope_t *current = scope->child; current != NULL; current = scope->next) {
__Q_FreeScopeRecuse( current );
}
QMutex_Destroy(&scope->lock);
Q_Free( scope->alloc );
Q_Free( scope );
}

void Q_FreeScope( memscope_t *scope )
{
if( !scope )
return;

if( scope->parent ) {
QMutex_Lock( scopeMutex );
struct memscope_s **current = &scope->parent->child;
while( *current && *current != scope ) {
current = &( *current )->next;
}
assert( *current == scope ); // pool was already freed
assert( current != NULL );
*current = scope->next;
QMutex_Unlock( scopeMutex );
}
__Q_FreeScopeRecuse( scope );
}

void _Mem_EmptyPool( mempool_t *pool, int musthave, int canthave, const char *filename, int fileline )
{
assert(pool);
Expand Down Expand Up @@ -1202,6 +1363,7 @@ void Memory_Init( void )
{
assert( !memory_initialized );

scopeMutex = QMutex_Create();
memMutex = QMutex_Create();

zoneMemPool = Mem_AllocPool( NULL, "Zone" );
Expand Down
68 changes: 56 additions & 12 deletions source/qcommon/mod_mem.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,15 @@

struct mempool_s;
typedef struct mempool_s mempool_t;
struct memscope_s;
typedef struct memscope_s memscope_t;

typedef void (*free_hander_t)(void* p);

struct memscope_alloc_s {
void* ptr;
void (*freeHandle)(void* ptr);
};

struct mempool_stats_s {
size_t size;
Expand Down Expand Up @@ -34,28 +43,55 @@ DECLARE_TYPEDEF_METHOD( void, Q_FreePool, mempool_t *pool );
DECLARE_TYPEDEF_METHOD( void, Q_EmptyPool, mempool_t *pool );
DECLARE_TYPEDEF_METHOD( struct mempool_stats_s, Q_PoolStats, mempool_t *pool );
DECLARE_TYPEDEF_METHOD( void, Mem_ValidationAllAllocations );


// similar to a pool but lighter weight constrction
// user is required to free memory from the scope or trigger a double free error from the memory tracker
DECLARE_TYPEDEF_METHOD( memscope_t*, Q_CreateScope, memscope_t *parent, const char* name);
DECLARE_TYPEDEF_METHOD( bool, Q_ScopeHas, memscope_t* scope, void* ptr);
DECLARE_TYPEDEF_METHOD( void, Q_ScopeAttach, memscope_t* scope, void* ptr); // take ownership of allocation
DECLARE_TYPEDEF_METHOD( void, Q_ScopeAttachWithFreeHandle, memscope_t *scope, free_hander_t handle, void *ptr ); // take ownership of allocation with a custom free
DECLARE_TYPEDEF_METHOD( void, Q_ScopeRelease, memscope_t* scope, void* ptr);
DECLARE_TYPEDEF_METHOD( void, Q_ScopeFree, memscope_t* scope, void* ptr); // free the memory attached to a scope
DECLARE_TYPEDEF_METHOD( void, Q_FreeScope, memscope_t* scope);


#undef DECLARE_TYPEDEF_METHOD

mempool_t* Q_ParentPool();

struct mem_import_s {
mempool_t* parent; // the parent pool to link to by default if NULL is passed into a pool
Q_PoolStatsFn Q_PoolStats;
__Q_MallocFn __Q_Malloc;
__Q_CallocFn __Q_Calloc;
__Q_ReallocFn __Q_Realloc;
__Q_MallocAlignedFn __Q_MallocAligned;
__Q_CallocAlignedFn __Q_CallocAligned;
Mem_ValidationAllAllocationsFn Mem_ValidationAllAllocations;
Q_FreeFn Q_Free;
Q_CreatePoolFn Q_CreatePool;
Q_LinkToPoolFn Q_LinkToPool;
Q_FreePoolFn Q_FreePool;
Q_EmptyPoolFn Q_EmptyPool;
Q_CreateScopeFn Q_CreateScope;
Q_ScopeHasFn Q_ScopeHas;
Q_ScopeAttachFn Q_ScopeAttach;
Q_ScopeAttachWithFreeHandleFn Q_ScopeAttachWithFreeHandle;
Q_ScopeReleaseFn Q_ScopeRelease;
Q_ScopeFreeFn Q_ScopeFree;
Q_FreeScopeFn Q_FreeScope;
Q_PoolStatsFn Q_PoolStats;
__Q_MallocFn __Q_Malloc;
__Q_CallocFn __Q_Calloc;
__Q_ReallocFn __Q_Realloc;
__Q_MallocAlignedFn __Q_MallocAligned;
__Q_CallocAlignedFn __Q_CallocAligned;
Mem_ValidationAllAllocationsFn Mem_ValidationAllAllocations;
Q_FreeFn Q_Free;
Q_CreatePoolFn Q_CreatePool;
Q_LinkToPoolFn Q_LinkToPool;
Q_FreePoolFn Q_FreePool;
Q_EmptyPoolFn Q_EmptyPool;
};

#define DECLARE_MEM_STRUCT(PARENT) { \
PARENT, \
Q_CreateScope, \
Q_ScopeHas, \
Q_ScopeAttach, \
Q_ScopeAttachWithFreeHandle, \
Q_ScopeRelease, \
Q_ScopeFree, \
Q_FreeScope, \
Q_PoolStats, \
__Q_Malloc, \
__Q_Calloc, \
Expand All @@ -73,6 +109,14 @@ struct mem_import_s {
#if MEM_DEFINE_INTERFACE_IMPL
static struct mem_import_s mem_import;
mempool_t* Q_ParentPool() { return mem_import.parent; }

memscope_t* Q_CreateScope(memscope_t *parent, const char* name) { return mem_import.Q_CreateScope(parent, name);}
bool Q_ScopeHas(memscope_t* scope, void* ptr) { return mem_import.Q_ScopeHas(scope, ptr);}
void Q_ScopeAttach(memscope_t* scope, void* ptr) { return mem_import.Q_ScopeAttach(scope, ptr);}
void Q_ScopeAttachWithFreeHandle(memscope_t *scope, free_hander_t handle, void *ptr ) { return mem_import.Q_ScopeAttachWithFreeHandle(scope, handle, ptr);}
void Q_ScopeRelease(memscope_t* scope, void* ptr) { return mem_import.Q_ScopeRelease(scope, ptr);}
void Q_ScopeFree(memscope_t* scope, void* ptr) { return mem_import.Q_ScopeFree(scope, ptr);}
void Q_FreeScope(memscope_t* scope) { return mem_import.Q_FreeScope(scope);}
void *__Q_Malloc( size_t size, const char *sourceFilename, const char *functionName, int sourceLine ) { return mem_import.__Q_Malloc(size, sourceFilename, functionName, sourceLine); }
void* __Q_Calloc(size_t count, size_t size, const char *sourceFilename, const char *functionName, int sourceLine ) { return mem_import.__Q_Calloc(count, size, sourceFilename, functionName, sourceLine); }
void *__Q_Realloc( void *ptr, size_t size, const char *sourceFilename, const char *functionName, int sourceLine ) { return mem_import.__Q_Realloc(ptr, size, sourceFilename, functionName, sourceLine); }
Expand Down
3 changes: 2 additions & 1 deletion source/ref_gl/r_light.c
Original file line number Diff line number Diff line change
Expand Up @@ -763,7 +763,8 @@ void R_InitLightStyles( model_t *mod )
assert( mod );

loadbmodel = (( mbrushmodel_t * )mod->extradata);
loadbmodel->superLightStyles = Mod_Malloc( mod, sizeof( *loadbmodel->superLightStyles ) * MAX_LIGHTSTYLES );
loadbmodel->superLightStyles = Q_CallocAligned( MAX_LIGHTSTYLES, 16, sizeof( *loadbmodel->superLightStyles ) );
Q_ScopeAttach(mod->scope, loadbmodel->superLightStyles);
loadbmodel->numSuperLightStyles = 0;

for( i = 0; i < MAX_LIGHTSTYLES; i++ )
Expand Down
Loading