/* ** Copyright 1991, 1992, Silicon Graphics, Inc. ** All Rights Reserved. ** ** This is UNPUBLISHED PROPRIETARY SOURCE CODE of Silicon Graphics, Inc.; ** the contents of this file may not be disclosed to third parties, copied or ** duplicated in any form, in whole or in part, without the prior written ** permission of Silicon Graphics, Inc. ** ** RESTRICTED RIGHTS LEGEND: ** Use, duplication or disclosure by the Government is subject to restrictions ** as set forth in subdivision (c)(1)(ii) of the Rights in Technical Data ** and Computer Software clause at DFARS 252.227-7013, and/or in similar or ** successor clauses in the FAR, DOD or NASA FAR Supplement. Unpublished - ** rights reserved under the Copyright Laws of the United States. */ #include "precomp.h" #pragma hdrstop #if DBG // Set glRandomMallocFail to a positive value, say 40, to enable random // allocation failures. The failure will occur every glRandomMallocFail // times. long glRandomMallocFail = 0; static long glRandomFailCount; // glSize is the size of memory in use. ULONG glSize = 0; ULONG glHighWater = 0; ULONG glReal = 0; static void AdjustSizes(LONG delta, void *mem) { ULONG nbytes; #ifdef GL_REAL_SIZE nbytes = HeapSize(GetProcessHeap(), 0, mem); #else nbytes = 0; #endif if (delta < 0) { glSize -= (ULONG)(-delta); glReal -= nbytes; if ((int) glSize < 0) { DBGPRINT("glSize underflows\n"); } } else if (delta > 0) { glSize += delta; glReal += nbytes; if ((int) glSize < 0) { DBGPRINT("glSize overflows\n"); } if (glSize > glHighWater) { #ifdef GL_SHOW_HIGH_WATER DbgPrint("glSize high %8d (%8d)\n", glSize, glReal); #endif glHighWater = glSize; } } } typedef struct _MEM_HDR { ULONG nbytes; ULONG signature[3]; } MEM_HDR; // 'GLal' in byte order #define MEM_ALLOC_SIG 0x6C614C47 // 'GLfr' in byte order #define MEM_FREE_SIG 0x72664C47 #define MEM_HDR_SIZE sizeof(MEM_HDR) #define MEM_HDR_PTR(mem) ((MEM_HDR *)((BYTE *)(mem)-MEM_HDR_SIZE)) // XXX We may want to protect these debug allocation functions with a // critical section. void * FASTCALL dbgAlloc(UINT nbytes, DWORD flags) { PVOID mem; // If random failure is enabled, fail this call randomly. if (glRandomMallocFail) { if (++glRandomFailCount >= glRandomMallocFail) { DBGPRINT("dbgAlloc random failing\n"); glRandomFailCount = 0; return NULL; } } if (nbytes == 0) { DBGERROR("nbytes == 0\n"); return NULL; } // Allocate extra bytes for debug house keeping. mem = HeapAlloc(GetProcessHeap(), flags, nbytes+MEM_HDR_SIZE); // Do house keeping and add allocation size so far. if (mem) { MEM_HDR *pmh = (MEM_HDR *)mem; pmh->nbytes = nbytes; pmh->signature[0] = MEM_ALLOC_SIG; pmh->signature[1] = MEM_ALLOC_SIG; pmh->signature[2] = MEM_ALLOC_SIG; AdjustSizes((LONG)nbytes, mem); mem = (PVOID) (pmh+1); } else { DBGLEVEL1(LEVEL_ERROR, "dbgAlloc could not allocate %u bytes\n", nbytes); } DBGLEVEL2(LEVEL_ALLOC, "dbgAlloc of %u returned 0x%x\n", nbytes, mem); return mem; } void FASTCALL dbgFree(void *mem) { MEM_HDR *pmh; if (!mem) { #ifdef FREE_OF_NULL_ERR // Freeing NULL happens currently so this error results // in a little too much spew. DBGERROR("mem is NULL\n"); #endif return; } // Verify that the signature is not corrupted. pmh = MEM_HDR_PTR(mem); if (pmh->signature[0] != MEM_ALLOC_SIG || pmh->signature[1] != MEM_ALLOC_SIG || pmh->signature[2] != MEM_ALLOC_SIG) { WARNING("Possible memory corruption\n"); } // Make sure it is freed once only. pmh->signature[0] = MEM_FREE_SIG; pmh->signature[1] = MEM_FREE_SIG; pmh->signature[2] = MEM_FREE_SIG; // Subtract the allocation size. AdjustSizes(-(LONG)pmh->nbytes, pmh); HeapFree(GetProcessHeap(), 0, pmh); DBGLEVEL1(LEVEL_ALLOC, "dbgFree of 0x%x\n", mem); } void * FASTCALL dbgRealloc(void *mem, UINT nbytes) { PVOID memNew; MEM_HDR *pmh; // If random failure is enabled, fail this call randomly. if (glRandomMallocFail) { if (++glRandomFailCount >= glRandomMallocFail) { DBGPRINT("dbgRealloc random failing\n"); glRandomFailCount = 0; return NULL; } } if (mem != NULL) { // Verify that the signature is not corrupted. pmh = MEM_HDR_PTR(mem); if (pmh->signature[0] != MEM_ALLOC_SIG || pmh->signature[1] != MEM_ALLOC_SIG || pmh->signature[2] != MEM_ALLOC_SIG) { WARNING("Possible memory corruption\n"); } AdjustSizes(-(LONG)pmh->nbytes, pmh); // Reallocate nbytes+extra bytes. memNew = HeapReAlloc(GetProcessHeap(), 0, pmh, nbytes+MEM_HDR_SIZE); } else { // Old memory pointer is NULL, so allocate a new chunk. memNew = HeapAlloc(GetProcessHeap(), 0, nbytes+MEM_HDR_SIZE); // We've allocated new memory so initialize its signature. if (memNew != NULL) { pmh = (MEM_HDR *)memNew; pmh->signature[0] = MEM_ALLOC_SIG; pmh->signature[1] = MEM_ALLOC_SIG; pmh->signature[2] = MEM_ALLOC_SIG; } } if (memNew != NULL) { // Do house keeping and update allocation size so far. AdjustSizes(nbytes, memNew); pmh = (MEM_HDR *)memNew; pmh->nbytes = nbytes; memNew = (PVOID) (pmh+1); } else { if (mem != NULL) { AdjustSizes((LONG)pmh->nbytes, pmh); } DBGLEVEL1(LEVEL_ERROR, "dbgRealloc could not allocate %u bytes\n", nbytes); } DBGLEVEL3(LEVEL_ALLOC, "dbgRealloc of 0x%X:%u returned 0x%x\n", mem, nbytes, memNew); return memNew; } int FASTCALL dbgMemSize(void *mem) { MEM_HDR *pmh; pmh = MEM_HDR_PTR(mem); if (pmh->signature[0] != MEM_ALLOC_SIG || pmh->signature[1] != MEM_ALLOC_SIG || pmh->signature[2] != MEM_ALLOC_SIG) { return -1; } return (int)pmh->nbytes; } #endif // DBG ULONG APIENTRY glDebugEntry(int param, void *data) { #if DBG switch(param) { case 0: return glSize; case 1: return glHighWater; case 2: return glReal; case 3: return dbgMemSize(data); } #endif return 0; } #define MEM_ALIGN 32 void * FASTCALL AllocAlign32(UINT nbytes) { void *mem; void **aligned; // We allocate enough extra memory for the alignment and our header // which just consists of a pointer: mem = ALLOC(nbytes + MEM_ALIGN + sizeof(void *)); if (!mem) { DBGLEVEL1(LEVEL_ERROR, "AllocAlign32 could not allocate %u bytes\n", nbytes); return NULL; } aligned = (void **)(((ULONG_PTR)mem + sizeof(void *) + (MEM_ALIGN - 1)) & ~(MEM_ALIGN - 1)); *(aligned-1) = mem; return aligned; } void FASTCALL FreeAlign32(void *mem) { if ( NULL == mem ) { DBGERROR("NULL pointer passed to FreeAlign32\n"); return; } FREE(*((void **)mem-1)); } void * FASTCALL gcAlloc( __GLcontext *gc, UINT nbytes, DWORD flags ) { void *mem; #if DBG mem = dbgAlloc(nbytes, flags); #else mem = HeapAlloc(GetProcessHeap(), flags, nbytes); #endif if (NULL == mem) { ((__GLGENcontext *)gc)->errorcode = GLGEN_OUT_OF_MEMORY; __glSetErrorEarly(gc, GL_OUT_OF_MEMORY); } return mem; } void * FASTCALL GCREALLOC( __GLcontext *gc, void *mem, UINT nbytes ) { void *newMem; // The Win32 realloc functions do not have free-on-zero behavior, // so fake it. if (nbytes == 0) { if (mem != NULL) { FREE(mem); } return NULL; } // The Win32 realloc functions don't handle a NULL old pointer, // so explicitly turn such calls into allocs. if (mem == NULL) { newMem = ALLOC(nbytes); } else { newMem = REALLOC(mem, nbytes); } if (NULL == newMem) { ((__GLGENcontext *)gc)->errorcode = GLGEN_OUT_OF_MEMORY; __glSetErrorEarly(gc, GL_OUT_OF_MEMORY); } return newMem; } void * FASTCALL GCALLOCALIGN32( __GLcontext *gc, UINT nbytes ) { void *mem; mem = AllocAlign32(nbytes); if (NULL == mem) { ((__GLGENcontext *)gc)->errorcode = GLGEN_OUT_OF_MEMORY; __glSetErrorEarly(gc, GL_OUT_OF_MEMORY); } return mem; } // Tunable parameters for temporary memory allocation #define MAX_TEMP_BUFFERS 4 #define TEMP_BUFFER_SIZE 4096 struct MemHeaderRec { LONG inUse; ULONG nbytes; void *mem; }; typedef struct MemHeaderRec MemHeader; MemHeader TempMemHeader[MAX_TEMP_BUFFERS]; // InitTempAlloc // Initializes the temporary memory allocation header and allocates the // temporary memory buffers. // // Synopsis: // BOOL InitTempAlloc() // // History: // 02-DEC-93 Eddie Robinson [v-eddier] Wrote it. // BOOL FASTCALL InitTempAlloc(void) { int i; PBYTE buffers; static LONG initCount = -1; if (initCount >= 0) return TRUE; if (InterlockedIncrement(&initCount) != 0) return TRUE; // Allocate buffers for the first time. buffers = ALLOC(MAX_TEMP_BUFFERS*TEMP_BUFFER_SIZE); if (!buffers) { InterlockedDecrement(&initCount); // try again later return FALSE; } for (i = 0; i < MAX_TEMP_BUFFERS; i++) { TempMemHeader[i].nbytes = TEMP_BUFFER_SIZE; TempMemHeader[i].mem = (void *) buffers; TempMemHeader[i].inUse = -1; // must be last buffers += TEMP_BUFFER_SIZE; } return TRUE; } // gcTempAlloc // Allocates temporary memory from a static array, if possible. Otherwise // it calls ALLOC // // Synopsis: // void * gcTempAlloc(__GLcontext *gc, UINT nbytes) // gc points to the OpenGL context structure // nbytes specifies the number of bytes to allocate // // History: // 02-DEC-93 Eddie Robinson [v-eddier] Wrote it. // void * FASTCALL gcTempAlloc(__GLcontext *gc, UINT nbytes) { int i; void *mem; if (nbytes == 0) { // Zero-byte allocations do occur so don't make this a warning // to avoid excessive debug spew. DBGLEVEL(LEVEL_ALLOC, "gcTempAlloc: failing zero byte alloc\n"); return NULL; } for (i = 0; i < MAX_TEMP_BUFFERS; i++) { if (nbytes <= TempMemHeader[i].nbytes) { if (InterlockedIncrement(&TempMemHeader[i].inUse)) { InterlockedDecrement(&TempMemHeader[i].inUse); } else { DBGLEVEL2(LEVEL_ALLOC, "gcTempAlloc of %u returned 0x%x\n", nbytes, TempMemHeader[i].mem); GC_TEMP_BUFFER_ALLOC(gc, TempMemHeader[i].mem); return(TempMemHeader[i].mem); } } } mem = ALLOC(nbytes); if (!mem) { WARNING1("gcTempAlloc: memory allocation error size %d\n", nbytes); ((__GLGENcontext *)gc)->errorcode = GLGEN_OUT_OF_MEMORY; __glSetErrorEarly(gc, GL_OUT_OF_MEMORY); } DBGLEVEL2(LEVEL_ALLOC, "gcTempAlloc of %u returned 0x%x\n", nbytes, mem); GC_TEMP_BUFFER_ALLOC(gc, mem); return mem; } // gcTempFree // Marks allocated static buffer as unused or calls FREE. // // Synopsis: // void gcTempFree(__GLcontext *gc, void *mem) // mem specifies the adress of the memory to free // // History: // 02-DEC-93 Eddie Robinson [v-eddier] Wrote it. // void FASTCALL gcTempFree(__GLcontext *gc, void *mem) { int i; DBGLEVEL1(LEVEL_ALLOC, "gcTempFree of 0x%x\n", mem); GC_TEMP_BUFFER_FREE(gc, mem); for (i = 0; i < MAX_TEMP_BUFFERS; i++) { if (mem == TempMemHeader[i].mem) { InterlockedDecrement(&TempMemHeader[i].inUse); return; } } FREE( mem ); }