X-Git-Url: http://de.git.xonotic.org/?p=xonotic%2Fdarkplaces.git;a=blobdiff_plain;f=zone.c;h=85a3b2199776b654539afc10499e9b08889fc7ff;hp=17e788abe2a02212a55348c61baaa8daf040a8f9;hb=46f08c84d7e41aafaac412fad5d26ee7b6405762;hpb=d3edab857eac367d4968f05f3e32ac2a9aa385b6 diff --git a/zone.c b/zone.c index 17e788ab..85a3b219 100644 --- a/zone.c +++ b/zone.c @@ -20,9 +20,11 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // Z_zone.c #include "quakedef.h" +#include "thread.h" #ifdef WIN32 #include +#include #else #include #endif @@ -35,15 +37,29 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #define MEMHEADER_SENTINEL_FOR_ADDRESS(p) ((sentinel_seed ^ (unsigned int) (uintptr_t) (p)) + sentinel_seed) unsigned int sentinel_seed; +qboolean mem_bigendian = false; +void *mem_mutex = NULL; + +// divVerent: enables file backed malloc using mmap to conserve swap space (instead of malloc) +#ifndef FILE_BACKED_MALLOC +# define FILE_BACKED_MALLOC 0 +#endif + // LordHavoc: enables our own low-level allocator (instead of malloc) -#define MEMCLUMPING 0 -#define MEMCLUMPING_FREECLUMPS 0 +#ifndef MEMCLUMPING +# define MEMCLUMPING 0 +#endif +#ifndef MEMCLUMPING_FREECLUMPS +# define MEMCLUMPING_FREECLUMPS 0 +#endif #if MEMCLUMPING // smallest unit we care about is this many bytes #define MEMUNIT 128 // try to do 32MB clumps, but overhead eats into this -#define MEMWANTCLUMPSIZE (1<<29) +#ifndef MEMWANTCLUMPSIZE +# define MEMWANTCLUMPSIZE (1<<27) +#endif // give malloc padding so we can't waste most of a page at the end #define MEMCLUMPSIZE (MEMWANTCLUMPSIZE - MEMWANTCLUMPSIZE/MEMUNIT/32 - 128) #define MEMBITS (MEMCLUMPSIZE / MEMUNIT) @@ -79,9 +95,57 @@ static memclump_t *clumpchain = NULL; cvar_t developer_memory = {0, "developer_memory", "0", "prints debugging information about memory allocations"}; cvar_t developer_memorydebug = {0, "developer_memorydebug", "0", "enables memory corruption checks (very slow)"}; +cvar_t sys_memsize_physical = {CVAR_READONLY, "sys_memsize_physical", "", "physical memory size in MB (or empty if unknown)"}; +cvar_t sys_memsize_virtual = {CVAR_READONLY, "sys_memsize_virtual", "", "virtual memory size in MB (or empty if unknown)"}; static mempool_t *poolchain = NULL; +void Mem_PrintStats(void); +void Mem_PrintList(size_t minallocationsize); + +#if FILE_BACKED_MALLOC +#include +#include +typedef struct mmap_data_s +{ + size_t len; +} +mmap_data_t; +#define MMAP_PAGE_SIZE max(sizeof(mmap_data_t), (size_t)sysconf(_SC_PAGE_SIZE)) +static void *mmap_malloc(size_t size) +{ + char vabuf[MAX_OSPATH + 1]; + char *tmpdir = getenv("TEMP"); + unsigned char *data; + int fd; + size += MMAP_PAGE_SIZE; // waste block + size += MMAP_PAGE_SIZE - 1; // also, waste up to this amount for management info + size -= (size % MMAP_PAGE_SIZE); // round down + dpsnprintf(vabuf, sizeof(vabuf), "%s/darkplaces.XXXXXX", tmpdir ? tmpdir : "/tmp"); + fd = mkstemp(vabuf); + if(fd < 0) + return NULL; + ftruncate(fd, size); + data = (unsigned char *) mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_NORESERVE, fd, 0); + close(fd); + unlink(vabuf); + if(!data) + return NULL; + ((mmap_data_t *) data)->len = size; + return (void *) (data + MMAP_PAGE_SIZE); +} +static void mmap_free(void *mem) +{ + unsigned char *data; + if(!mem) + return; + data = (unsigned char *) mem - MMAP_PAGE_SIZE; + munmap(data, ((mmap_data_t *) data)->len); +} +#define malloc mmap_malloc +#define free mmap_free +#endif + #if MEMCLUMPING != 2 // some platforms have a malloc that returns NULL but succeeds later // (Windows growing its swapfile for example) @@ -95,11 +159,7 @@ static void *attempt_malloc(size_t size) base = (void *)malloc(size); if (base) return base; -#ifdef WIN32 - Sleep(1); -#else - usleep(1000); -#endif + Sys_Sleep(1000); } return NULL; } @@ -307,26 +367,46 @@ static void Clump_FreeBlock(void *base, size_t size) #endif } -void *_Mem_Alloc(mempool_t *pool, size_t size, const char *filename, int fileline) +void *_Mem_Alloc(mempool_t *pool, void *olddata, size_t size, size_t alignment, const char *filename, int fileline) { unsigned int sentinel1; unsigned int sentinel2; size_t realsize; + size_t sharedsize; + size_t remainsize; memheader_t *mem; + memheader_t *oldmem; + unsigned char *base; + if (size <= 0) + { + if (olddata) + _Mem_Free(olddata, filename, fileline); return NULL; + } if (pool == NULL) Sys_Error("Mem_Alloc: pool == NULL (alloc at %s:%i)", filename, fileline); - if (developer.integer && developer_memory.integer) - Con_Printf("Mem_Alloc: pool %s, file %s:%i, size %i bytes\n", pool->name, filename, fileline, (int)size); - //if (developer.integer && developer_memorydebug.integer) + if (mem_mutex) + Thread_LockMutex(mem_mutex); + if (developer_memory.integer) + Con_DPrintf("Mem_Alloc: pool %s, file %s:%i, size %i bytes\n", pool->name, filename, fileline, (int)size); + //if (developer.integer > 0 && developer_memorydebug.integer) // _Mem_CheckSentinelsGlobal(filename, fileline); pool->totalsize += size; - realsize = sizeof(memheader_t) + size + sizeof(sentinel2); + realsize = alignment + sizeof(memheader_t) + size + sizeof(sentinel2); pool->realsize += realsize; - mem = (memheader_t *)Clump_AllocBlock(realsize); - if (mem == NULL) + base = (unsigned char *)Clump_AllocBlock(realsize); + if (base== NULL) + { + Mem_PrintList(0); + Mem_PrintStats(); + Mem_PrintList(1<<30); + Mem_PrintStats(); Sys_Error("Mem_Alloc: out of memory (alloc at %s:%i)", filename, fileline); + } + // calculate address that aligns the end of the memheader_t to the specified alignment + mem = (memheader_t*)((((size_t)base + sizeof(memheader_t) + (alignment-1)) & ~(alignment-1)) - sizeof(memheader_t)); + mem->baseaddress = (void*)base; mem->filename = filename; mem->fileline = fileline; mem->size = size; @@ -344,7 +424,22 @@ void *_Mem_Alloc(mempool_t *pool, size_t size, const char *filename, int filelin pool->chain = mem; if (mem->next) mem->next->prev = mem; - memset((void *)((unsigned char *) mem + sizeof(memheader_t)), 0, mem->size); + + if (mem_mutex) + Thread_UnlockMutex(mem_mutex); + + // copy the shared portion in the case of a realloc, then memset the rest + sharedsize = 0; + remainsize = size; + if (olddata) + { + oldmem = (memheader_t*)olddata - 1; + sharedsize = min(oldmem->size, size); + memcpy((void *)((unsigned char *) mem + sizeof(memheader_t)), olddata, sharedsize); + remainsize -= sharedsize; + _Mem_Free(olddata, filename, fileline); + } + memset((void *)((unsigned char *) mem + sizeof(memheader_t) + sharedsize), 0, remainsize); return (void *)((unsigned char *) mem + sizeof(memheader_t)); } @@ -361,16 +456,18 @@ static void _Mem_FreeBlock(memheader_t *mem, const char *filename, int fileline) sentinel1 = MEMHEADER_SENTINEL_FOR_ADDRESS(&mem->sentinel); sentinel2 = MEMHEADER_SENTINEL_FOR_ADDRESS((unsigned char *) mem + sizeof(memheader_t) + mem->size); if (mem->sentinel != sentinel1) - Sys_Error("Mem_Free: trashed header sentinel 1 (alloc at %s:%i, free at %s:%i)", mem->filename, mem->fileline, filename, fileline); + Sys_Error("Mem_Free: trashed head sentinel (alloc at %s:%i, free at %s:%i)", mem->filename, mem->fileline, filename, fileline); if (memcmp((unsigned char *) mem + sizeof(memheader_t) + mem->size, &sentinel2, sizeof(sentinel2))) - Sys_Error("Mem_Free: trashed header sentinel 2 (alloc at %s:%i, free at %s:%i)", mem->filename, mem->fileline, filename, fileline); + Sys_Error("Mem_Free: trashed tail sentinel (alloc at %s:%i, free at %s:%i)", mem->filename, mem->fileline, filename, fileline); pool = mem->pool; - if (developer.integer && developer_memory.integer) - Con_Printf("Mem_Free: pool %s, alloc %s:%i, free %s:%i, size %i bytes\n", pool->name, mem->filename, mem->fileline, filename, fileline, (int)(mem->size)); + if (developer_memory.integer) + Con_DPrintf("Mem_Free: pool %s, alloc %s:%i, free %s:%i, size %i bytes\n", pool->name, mem->filename, mem->fileline, filename, fileline, (int)(mem->size)); // unlink memheader from doubly linked list if ((mem->prev ? mem->prev->next != mem : pool->chain != mem) || (mem->next && mem->next->prev != mem)) Sys_Error("Mem_Free: not allocated or double freed (free at %s:%i)", filename, fileline); + if (mem_mutex) + Thread_LockMutex(mem_mutex); if (mem->prev) mem->prev->next = mem->next; else @@ -382,7 +479,9 @@ static void _Mem_FreeBlock(memheader_t *mem, const char *filename, int fileline) realsize = sizeof(memheader_t) + size + sizeof(sentinel2); pool->totalsize -= size; pool->realsize -= realsize; - Clump_FreeBlock(mem, realsize); + Clump_FreeBlock(mem->baseaddress, realsize); + if (mem_mutex) + Thread_UnlockMutex(mem_mutex); } void _Mem_Free(void *data, const char *filename, int fileline) @@ -393,7 +492,7 @@ void _Mem_Free(void *data, const char *filename, int fileline) return; } - if (developer.integer && developer_memorydebug.integer) + if (developer_memorydebug.integer) { //_Mem_CheckSentinelsGlobal(filename, fileline); if (!Mem_IsAllocated(NULL, data)) @@ -406,11 +505,17 @@ void _Mem_Free(void *data, const char *filename, int fileline) mempool_t *_Mem_AllocPool(const char *name, int flags, mempool_t *parent, const char *filename, int fileline) { mempool_t *pool; - //if (developer.integer && developer_memorydebug.integer) - // _Mem_CheckSentinelsGlobal(filename, fileline); + if (developer_memorydebug.integer) + _Mem_CheckSentinelsGlobal(filename, fileline); pool = (mempool_t *)Clump_AllocBlock(sizeof(mempool_t)); if (pool == NULL) + { + Mem_PrintList(0); + Mem_PrintStats(); + Mem_PrintList(1<<30); + Mem_PrintStats(); Sys_Error("Mem_AllocPool: out of memory (allocpool at %s:%i)", filename, fileline); + } memset(pool, 0, sizeof(mempool_t)); pool->sentinel1 = MEMHEADER_SENTINEL_FOR_ADDRESS(&pool->sentinel1); pool->sentinel2 = MEMHEADER_SENTINEL_FOR_ADDRESS(&pool->sentinel2); @@ -432,8 +537,8 @@ void _Mem_FreePool(mempool_t **poolpointer, const char *filename, int fileline) mempool_t *pool = *poolpointer; mempool_t **chainaddress, *iter, *temp; - //if (developer.integer && developer_memorydebug.integer) - // _Mem_CheckSentinelsGlobal(filename, fileline); + if (developer_memorydebug.integer) + _Mem_CheckSentinelsGlobal(filename, fileline); if (pool) { // unlink pool from chain @@ -466,7 +571,7 @@ void _Mem_EmptyPool(mempool_t *pool, const char *filename, int fileline) { mempool_t *chainaddress; - if (developer.integer && developer_memorydebug.integer) + if (developer_memorydebug.integer) { //_Mem_CheckSentinelsGlobal(filename, fileline); // check if this pool is in the poolchain @@ -479,9 +584,9 @@ void _Mem_EmptyPool(mempool_t *pool, const char *filename, int fileline) if (pool == NULL) Sys_Error("Mem_EmptyPool: pool == NULL (emptypool at %s:%i)", filename, fileline); if (pool->sentinel1 != MEMHEADER_SENTINEL_FOR_ADDRESS(&pool->sentinel1)) - Sys_Error("Mem_EmptyPool: trashed pool sentinel 2 (allocpool at %s:%i, emptypool at %s:%i)", pool->filename, pool->fileline, filename, fileline); - if (pool->sentinel2 != MEMHEADER_SENTINEL_FOR_ADDRESS(&pool->sentinel2)) Sys_Error("Mem_EmptyPool: trashed pool sentinel 1 (allocpool at %s:%i, emptypool at %s:%i)", pool->filename, pool->fileline, filename, fileline); + if (pool->sentinel2 != MEMHEADER_SENTINEL_FOR_ADDRESS(&pool->sentinel2)) + Sys_Error("Mem_EmptyPool: trashed pool sentinel 2 (allocpool at %s:%i, emptypool at %s:%i)", pool->filename, pool->fileline, filename, fileline); // free memory owned by the pool while (pool->chain) @@ -507,9 +612,9 @@ void _Mem_CheckSentinels(void *data, const char *filename, int fileline) sentinel1 = MEMHEADER_SENTINEL_FOR_ADDRESS(&mem->sentinel); sentinel2 = MEMHEADER_SENTINEL_FOR_ADDRESS((unsigned char *) mem + sizeof(memheader_t) + mem->size); if (mem->sentinel != sentinel1) - Sys_Error("Mem_Free: trashed header sentinel 1 (alloc at %s:%i, sentinel check at %s:%i)", mem->filename, mem->fileline, filename, fileline); + Sys_Error("Mem_Free: trashed head sentinel (alloc at %s:%i, sentinel check at %s:%i)", mem->filename, mem->fileline, filename, fileline); if (memcmp((unsigned char *) mem + sizeof(memheader_t) + mem->size, &sentinel2, sizeof(sentinel2))) - Sys_Error("Mem_Free: trashed header sentinel 2 (alloc at %s:%i, sentinel check at %s:%i)", mem->filename, mem->fileline, filename, fileline); + Sys_Error("Mem_Free: trashed tail sentinel (alloc at %s:%i, sentinel check at %s:%i)", mem->filename, mem->fileline, filename, fileline); } #if MEMCLUMPING @@ -590,6 +695,44 @@ void Mem_ExpandableArray_FreeArray(memexpandablearray_t *l) memset(l, 0, sizeof(*l)); } +// VorteX: hacked Mem_ExpandableArray_AllocRecord, it does allocate record at certain index +void *Mem_ExpandableArray_AllocRecordAtIndex(memexpandablearray_t *l, size_t index) +{ + size_t j; + if (index >= l->numarrays) + { + if (l->numarrays == l->maxarrays) + { + memexpandablearray_array_t *oldarrays = l->arrays; + l->maxarrays = max(l->maxarrays * 2, 128); + l->arrays = (memexpandablearray_array_t*) Mem_Alloc(l->mempool, l->maxarrays * sizeof(*l->arrays)); + if (oldarrays) + { + memcpy(l->arrays, oldarrays, l->numarrays * sizeof(*l->arrays)); + Mem_Free(oldarrays); + } + } + l->arrays[index].numflaggedrecords = 0; + l->arrays[index].data = (unsigned char *) Mem_Alloc(l->mempool, (l->recordsize + 1) * l->numrecordsperarray); + l->arrays[index].allocflags = l->arrays[index].data + l->recordsize * l->numrecordsperarray; + l->numarrays++; + } + if (l->arrays[index].numflaggedrecords < l->numrecordsperarray) + { + for (j = 0;j < l->numrecordsperarray;j++) + { + if (!l->arrays[index].allocflags[j]) + { + l->arrays[index].allocflags[j] = true; + l->arrays[index].numflaggedrecords++; + memset(l->arrays[index].data + l->recordsize * j, 0, l->recordsize); + return (void *)(l->arrays[index].data + l->recordsize * j); + } + } + } + return NULL; +} + void *Mem_ExpandableArray_AllocRecord(memexpandablearray_t *l) { size_t i, j; @@ -727,7 +870,7 @@ void Mem_PrintList(size_t minallocationsize) "size name\n"); for (pool = poolchain;pool;pool = pool->next) { - Con_Printf("%10luk (%10luk actual) %s (%+li byte change) %s\n", (unsigned long) ((pool->totalsize + 1023) / 1024), (unsigned long)((pool->realsize + 1023) / 1024), pool->name, (long)pool->totalsize - pool->lastchecksize, (pool->flags & POOLFLAG_TEMP) ? "TEMP" : ""); + Con_Printf("%10luk (%10luk actual) %s (%+li byte change) %s\n", (unsigned long) ((pool->totalsize + 1023) / 1024), (unsigned long)((pool->realsize + 1023) / 1024), pool->name, (long)(pool->totalsize - pool->lastchecksize), (pool->flags & POOLFLAG_TEMP) ? "TEMP" : ""); pool->lastchecksize = pool->totalsize; for (mem = pool->chain;mem;mem = mem->next) if (mem->size >= minallocationsize) @@ -735,7 +878,7 @@ void Mem_PrintList(size_t minallocationsize) } } -void MemList_f(void) +static void MemList_f(void) { switch(Cmd_Argc()) { @@ -753,8 +896,7 @@ void MemList_f(void) } } -extern void R_TextureStats_Print(qboolean printeach, qboolean printpool, qboolean printtotal); -void MemStats_f(void) +static void MemStats_f(void) { Mem_CheckSentinelsGlobal(); R_TextureStats_Print(false, false, true); @@ -766,8 +908,10 @@ void MemStats_f(void) char* Mem_strdup (mempool_t *pool, const char* s) { char* p; - size_t sz = strlen (s) + 1; - if (s == NULL) return NULL; + size_t sz; + if (s == NULL) + return NULL; + sz = strlen (s) + 1; p = (char*)Mem_Alloc (pool, sz); strlcpy (p, s, sz); return p; @@ -780,16 +924,27 @@ Memory_Init */ void Memory_Init (void) { + static union {unsigned short s;unsigned char b[2];} u; + u.s = 0x100; + mem_bigendian = u.b[0] != 0; + sentinel_seed = rand(); poolchain = NULL; tempmempool = Mem_AllocPool("Temporary Memory", POOLFLAG_TEMP, NULL); zonemempool = Mem_AllocPool("Zone", 0, NULL); + + if (Thread_HasThreads()) + mem_mutex = Thread_CreateMutex(); } void Memory_Shutdown (void) { // Mem_FreePool (&zonemempool); // Mem_FreePool (&tempmempool); + + if (mem_mutex) + Thread_DestroyMutex(mem_mutex); + mem_mutex = NULL; } void Memory_Init_Commands (void) @@ -798,5 +953,68 @@ void Memory_Init_Commands (void) Cmd_AddCommand ("memlist", MemList_f, "prints memory pool information (or if used as memlist 5 lists individual allocations of 5K or larger, 0 lists all allocations)"); Cvar_RegisterVariable (&developer_memory); Cvar_RegisterVariable (&developer_memorydebug); + Cvar_RegisterVariable (&sys_memsize_physical); + Cvar_RegisterVariable (&sys_memsize_virtual); + +#if defined(WIN32) +#ifdef _WIN64 + { + MEMORYSTATUSEX status; + // first guess + Cvar_SetValueQuick(&sys_memsize_virtual, 8388608); + // then improve + status.dwLength = sizeof(status); + if(GlobalMemoryStatusEx(&status)) + { + Cvar_SetValueQuick(&sys_memsize_physical, status.ullTotalPhys / 1048576.0); + Cvar_SetValueQuick(&sys_memsize_virtual, min(sys_memsize_virtual.value, status.ullTotalVirtual / 1048576.0)); + } + } +#else + { + MEMORYSTATUS status; + // first guess + Cvar_SetValueQuick(&sys_memsize_virtual, 2048); + // then improve + status.dwLength = sizeof(status); + GlobalMemoryStatus(&status); + Cvar_SetValueQuick(&sys_memsize_physical, status.dwTotalPhys / 1048576.0); + Cvar_SetValueQuick(&sys_memsize_virtual, min(sys_memsize_virtual.value, status.dwTotalVirtual / 1048576.0)); + } +#endif +#else + { + // first guess + Cvar_SetValueQuick(&sys_memsize_virtual, (sizeof(void*) == 4) ? 2048 : 268435456); + // then improve + { + // Linux, and BSD with linprocfs mounted + FILE *f = fopen("/proc/meminfo", "r"); + if(f) + { + static char buf[1024]; + while(fgets(buf, sizeof(buf), f)) + { + const char *p = buf; + if(!COM_ParseToken_Console(&p)) + continue; + if(!strcmp(com_token, "MemTotal:")) + { + if(!COM_ParseToken_Console(&p)) + continue; + Cvar_SetValueQuick(&sys_memsize_physical, atof(com_token) / 1024.0); + } + if(!strcmp(com_token, "SwapTotal:")) + { + if(!COM_ParseToken_Console(&p)) + continue; + Cvar_SetValueQuick(&sys_memsize_virtual, min(sys_memsize_virtual.value , atof(com_token) / 1024.0 + sys_memsize_physical.value)); + } + } + fclose(f); + } + } + } +#endif }