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