/* * tclMacAlloc.c -- * * This is a very fast storage allocator. It allocates blocks of a * small number of different sizes, and keeps free lists of each size. * Blocks that don't exactly fit are passed up to the next larger size. * Blocks over a certain size are directly allocated by calling NewPtr. * * Copyright (c) 1983 Regents of the University of California. * Copyright (c) 1996-1997 Sun Microsystems, Inc. * * Portions contributed by Chris Kingsley, Jack Jansen and Ray Johnson *. * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * * RCS: @(#) $Id: tclMacAlloc.c,v 1.5 2001/11/23 01:27:09 das Exp $ */ #include "tclInt.h" #include "tclMacInt.h" #include #include #include #include /* * Flags that are used by ConfigureMemory to define how the allocator * should work. They can be or'd together. */ #define MEMORY_ALL_SYS 1 /* All memory should come from the system heap. */ #define MEMORY_DONT_USE_TEMPMEM 2 /* Don't use temporary memory but system memory. */ /* * Amount of space to leave in the application heap for the Toolbox to work. */ #define TOOLBOX_SPACE (512 * 1024) static int memoryFlags = 0; static Handle toolGuardHandle = NULL; /* This handle must be around so that we don't * have NewGWorld failures. This handle is * purgeable. Before we allocate any blocks, * we see if this handle is still around. * If it is not, then we try to get it again. * If we can get it, we lock it and try * to do the normal allocation, unlocking on * the way out. If we can't, we go to the * system heap directly. */ static int tclUseMemTracking = 0; /* Are we tracking memory allocations? * On recent versions of the MacOS this * is no longer necessary, as we can use * temporary memory which is freed by the * OS after a quit or crash. */ static size_t tclExtraHdlSize = 0; /* Size of extra memory allocated at the start * of each block when using memory tracking * ( == 0 otherwise) */ /* * The following typedef and variable are used to keep track of memory * blocks that are allocated directly from the System Heap. These chunks * of memory must always be freed - even if we crash. */ typedef struct listEl { Handle memoryHandle; struct listEl * next; struct listEl * prec; } ListEl; static ListEl * systemMemory = NULL; static ListEl * appMemory = NULL; /* * Prototypes for functions used only in this file. */ static pascal void CleanUpExitProc _ANSI_ARGS_((void)); void ConfigureMemory _ANSI_ARGS_((int flags)); void FreeAllMemory _ANSI_ARGS_((void)); /* *---------------------------------------------------------------------- * * TclpSysRealloc -- * * This function reallocates a chunk of system memory. If the * chunk is already big enough to hold the new block, then no * allocation happens. * * Results: * Returns a pointer to the newly allocated block. * * Side effects: * May copy the contents of the original block to the new block * and deallocate the original block. * *---------------------------------------------------------------------- */ VOID * TclpSysRealloc( VOID *oldPtr, /* Original block */ unsigned int size) /* New size of block. */ { Handle hand; void *newPtr; int maxsize; OSErr err; if (tclUseMemTracking) { hand = ((ListEl *) ((Ptr) oldPtr - tclExtraHdlSize))->memoryHandle; } else { hand = RecoverHandle((Ptr) oldPtr); } maxsize = GetHandleSize(hand) - sizeof(Handle); if (maxsize < size) { HUnlock(hand); SetHandleSize(hand,size + tclExtraHdlSize); err = MemError(); HLock(hand); if(err==noErr){ newPtr=(*hand + tclExtraHdlSize); } else { newPtr = TclpSysAlloc(size, 1); if(newPtr!=NULL) { memmove(newPtr, oldPtr, maxsize); TclpSysFree(oldPtr); } } } else { newPtr = oldPtr; } return newPtr; } /* *---------------------------------------------------------------------- * * TclpSysAlloc -- * * Allocate a new block of memory free from the System. * * Results: * Returns a pointer to a new block of memory. * * Side effects: * May obtain memory from app or sys space. Info is added to * overhead lists etc. * *---------------------------------------------------------------------- */ VOID * TclpSysAlloc( long size, /* Size of block to allocate. */ int isBin) /* Is this a bin allocation? */ { Handle hand = NULL; ListEl * newMemoryRecord; int isSysMem = 0; static int initialized=0; if (!initialized) { long response = 0; OSErr err = noErr; int useTempMem = 0; /* Check if we can use temporary memory */ initialized=1; err = Gestalt(gestaltOSAttr, &response); if (err == noErr) { useTempMem = response & (1 << gestaltRealTempMemory); } tclUseMemTracking = !useTempMem || (memoryFlags & MEMORY_DONT_USE_TEMPMEM); if(tclUseMemTracking) { tclExtraHdlSize = sizeof(ListEl); /* * We are allocating memory directly from the system * heap. We need to install an exit handle * to ensure the memory is cleaned up. */ TclMacInstallExitToShellPatch(CleanUpExitProc); } } if (!(memoryFlags & MEMORY_ALL_SYS)) { /* * If the guard handle has been purged, throw it away and try * to allocate it again. */ if ((toolGuardHandle != NULL) && (*toolGuardHandle == NULL)) { DisposeHandle(toolGuardHandle); toolGuardHandle = NULL; } /* * If we have never allocated the guard handle, or it was purged * and thrown away, then try to allocate it again. */ if (toolGuardHandle == NULL) { toolGuardHandle = NewHandle(TOOLBOX_SPACE); if (toolGuardHandle != NULL) { HLock(toolGuardHandle); HPurge(toolGuardHandle); } } /* * If we got the handle, lock it and do our allocation. */ if (toolGuardHandle != NULL) { HLock(toolGuardHandle); hand = NewHandle(size + tclExtraHdlSize); HUnlock(toolGuardHandle); } } if (hand == NULL) { /* * Ran out of memory in application space. Lets try to get * more memory from system. Otherwise, we return NULL to * denote failure. */ if(!tclUseMemTracking) { /* Use Temporary Memory instead of System Heap when available */ OSErr err; isBin = 1; /* always HLockHi TempMemHandles */ hand = TempNewHandle(size + tclExtraHdlSize,&err); if(err!=noErr) { hand=NULL; } } else { /* Use system heap when tracking memory */ isSysMem=1; isBin = 0; hand = NewHandleSys(size + tclExtraHdlSize); } } if (hand == NULL) { return NULL; } if (isBin) { HLockHi(hand); } else { HLock(hand); } if(tclUseMemTracking) { /* Only need to do this when tracking memory */ newMemoryRecord = (ListEl *) *hand; newMemoryRecord->memoryHandle = hand; newMemoryRecord->prec = NULL; if(isSysMem) { newMemoryRecord->next = systemMemory; systemMemory = newMemoryRecord; } else { newMemoryRecord->next = appMemory; appMemory = newMemoryRecord; } if(newMemoryRecord->next!=NULL) { newMemoryRecord->next->prec=newMemoryRecord; } } return (*hand + tclExtraHdlSize); } /* *---------------------------------------------------------------------- * * TclpSysFree -- * * Free memory that we allocated back to the system. * * Results: * None. * * Side effects: * Memory is freed. * *---------------------------------------------------------------------- */ void TclpSysFree( void * ptr) /* Free this system memory. */ { if(tclUseMemTracking) { /* Only need to do this when tracking memory */ ListEl *memRecord; memRecord = (ListEl *) ((Ptr) ptr - tclExtraHdlSize); /* Remove current record from linked list */ if(memRecord->next!=NULL) { memRecord->next->prec=memRecord->prec; } if(memRecord->prec!=NULL) { memRecord->prec->next=memRecord->next; } if(memRecord==appMemory) { appMemory=memRecord->next; } else if(memRecord==systemMemory) { systemMemory=memRecord->next; } DisposeHandle(memRecord->memoryHandle); } else { DisposeHandle(RecoverHandle((Ptr) ptr)); } } /* *---------------------------------------------------------------------- * * CleanUpExitProc -- * * This procedure is invoked as an exit handler when ExitToShell * is called. It removes any memory that was allocated directly * from the system heap. This must be called when the application * quits or the memory will never be freed. * * Results: * None. * * Side effects: * May free memory in the system heap. * *---------------------------------------------------------------------- */ static pascal void CleanUpExitProc() { ListEl * memRecord; if(tclUseMemTracking) { /* Only need to do this when tracking memory */ while (systemMemory != NULL) { memRecord = systemMemory; systemMemory = memRecord->next; DisposeHandle(memRecord->memoryHandle); } } } /* *---------------------------------------------------------------------- * * FreeAllMemory -- * * This procedure frees all memory blocks allocated by the memory * sub-system. Make sure you don't have any code that references * any malloced data! * * Results: * None. * * Side effects: * Frees all memory allocated by TclpAlloc. * *---------------------------------------------------------------------- */ void FreeAllMemory() { ListEl * memRecord; if(tclUseMemTracking) { /* Only need to do this when tracking memory */ while (systemMemory != NULL) { memRecord = systemMemory; systemMemory = memRecord->next; DisposeHandle(memRecord->memoryHandle); } while (appMemory != NULL) { memRecord = appMemory; appMemory = memRecord->next; DisposeHandle(memRecord->memoryHandle); } } } /* *---------------------------------------------------------------------- * * ConfigureMemory -- * * This procedure sets certain flags in this file that control * how memory is allocated and managed. This call must be made * before any call to TclpAlloc is made. * * Results: * None. * * Side effects: * Certain state will be changed. * *---------------------------------------------------------------------- */ void ConfigureMemory( int flags) /* Flags that control memory alloc scheme. */ { memoryFlags = flags; }