Retired Document
Important: This sample code may not represent best practices for current development. The project may use deprecated symbols and illustrate technologies and techniques that are no longer recommended.
EmergMem.c
/* |
File: EmergMem.c |
Contains: Source code for the emergency memory routines |
Written by: Forrest Tanaka |
Copyright: Copyright © 1988-1999 by Apple Computer, Inc., All Rights Reserved. |
You may incorporate this Apple sample source code into your program(s) without |
restriction. This Apple sample source code has been provided "AS IS" and the |
responsibility for its operation is yours. You are not permitted to redistribute |
this Apple sample source code as "Apple sample source code" after having made |
changes. If you're going to re-distribute the source, we require that you make |
it clear in the source that the code was descended from Apple sample source |
code, but that you've made changes. |
Change History (most recent first): |
7/13/1999 Karl Groethe Updated for Metrowerks Codewarror Pro 2.1 |
*/ |
/******************************************************************************\ |
* Header Files |
\******************************************************************************/ |
#ifndef THINK_C |
#include <OSUtils.h> |
#endif |
#include "EmergMem.h" |
/******************************************************************************\ |
* Constants |
\******************************************************************************/ |
#define kEmergMemSize 32768 /* Number of bytes of emergency mem to allocate */ |
#define kMemoryMargin 32768 /* Òsignificant amountÓ amount of free mem */ |
/******************************************************************************\ |
* Global Variables |
\******************************************************************************/ |
Handle gEmergMem; /* Handle to block of emergency memory */ |
/******************************************************************************\ |
* Private: AppGrowZone - Custom grow-zone procedure |
* |
* This is a very basic grow zone procedure. My application keeps a reserve |
* handle of memory in case the Memory Manager gets a request for some memory |
* that is not available in my heap. If memory were to get tight (<32k or so), |
* the Toolbox could crash the system. This grow-zone proc tries to thwart that |
* possibility by releasing the 32K block of emergency memory if it hasnÕt been |
* released already and if the amount of memory requested is less than 32K. |
* Hopefully, thatÕs enough to satisfy the memory request. |
* |
* There are three conditions in which the emergency memory isnÕt freed. If the |
* emergency memory is already free, obviously there isnÕt much that can be done. |
* If the emergency memory is equal to GZSaveHnd, then it was the reallocation of |
* emergency memory that caused this grow-zone proc to be called. So it doesnÕt |
* make much sense to free it in that case. If the size of the memory request is |
* more than the size of emergency memory, then I donÕt bother to free emergency |
* memory because I assume that the toolbox handles such huge requests for memory |
* properly. Warning: that isnÕt always a good assumption, but thatÕs not my |
* fault. |
* |
* WARNING: Register A5 might not be valid when grow-zone procedures |
* are called. Read Technical Note #136 and 208. |
* |
* The "cbNeeded" parameter is the number of bytes that the Memory Manager needs |
* to fulfill the memory request it had received. The number of bytes actually |
* freed by AppGrowZone is returned. |
\******************************************************************************/ |
static pascal long AppGrowZone( |
Size cbNeeded) /* Number of bytes needed by Memory Manager */ |
{ |
long theA5; /* Value of A5 when AppGrowZone is called */ |
long amountFreed; /* Number of bytes freed up */ |
/* Remember the current value of A5 */ |
theA5 = SetCurrentA5(); |
/* Free emergency memory if possible */ |
if (!NoEmergMem() && (gEmergMem != GZSaveHnd()) && |
(cbNeeded <= kEmergMemSize)) |
{ |
EmptyHandle( gEmergMem ); |
amountFreed = kEmergMemSize; |
} |
else |
amountFreed = 0; |
/* Restore A5 */ |
(void)SetA5( theA5 ); |
/* Return number of bytes freed */ |
return amountFreed; |
} |
/******************************************************************************\ |
* Public: InstallAppGZ |
* |
* Installing our custom grow-zone procedure simply involves calling SetGrowZone |
* with the address of our custom grow-zone procedure. |
\******************************************************************************/ |
void InstallAppGZ() |
{ |
SetGrowZone( NewGrowZoneProc(AppGrowZone) ); |
} |
/******************************************************************************\ |
* Public: DeinstallAppGZ |
* |
* Passing NIL to SetGrowZone is all thatÕs needed to tell the Memory Manager not |
* to call a grow-zone procedure whenenver memory requests canÕt be satisfied. |
\******************************************************************************/ |
void DeinstallAppGZ() |
{ |
SetGrowZone( nil ); |
} |
/******************************************************************************\ |
* Public: InitEmergMem |
* |
* If the block of emergency memory couldnÕt be allocated, then weÕre probably in |
* some pretty big trouble. But InitEmergMem tries to deal as best it can in |
* that case by allocating an empty handle; that is, it allocates a master |
* pointer thatÕs set to NIL. That leaves it up to the rest of the application |
* to decide what to do. |
\******************************************************************************/ |
void InitEmergMem() |
{ |
/* Allocate the block of emergency memory */ |
gEmergMem = NewHandle( kEmergMemSize ); |
if (gEmergMem == nil) |
/* CouldnÕt allocate emergency memory; just allocate an empty handle */ |
gEmergMem = NewEmptyHandle(); |
/* Now that emergency memory is initialized, can install grow zone proc */ |
InstallAppGZ(); |
} |
/******************************************************************************\ |
* Public: RecoverEmergMem |
* |
* ReallocHandle takes an existing empty handle (handle whose master pointer is |
* nil) and allocates a block of memory for it. Perfect for this job! |
\******************************************************************************/ |
void RecoverEmergMem() |
{ |
ReallocateHandle( gEmergMem, kEmergMemSize ); |
} |
/******************************************************************************\ |
* Public: FailLowMemory |
* |
* PurgeSpace is used to determine how much free memory thereÕd be in the heap if |
* all purgeable blocks were purged. If this amount is less than the amount |
* needed, or if there isnÕt any emergency memory, true is returned. |
\******************************************************************************/ |
Boolean FailLowMemory( |
long memRequest) /* Amount of memory to check */ |
{ |
long total; /* Total amount of free memory if heap was purged */ |
long contig; /* Max amount of free contiguous memory if heap was purged */ |
PurgeSpace( /*<*/&total, /*<*/&contig ); |
return (total < (memRequest + kMemoryMargin)) || NoEmergMem(); |
} |
/******************************************************************************\ |
* Public: NoEmergMem |
* |
* We check on the handle and the master pointer of gEmergMem to see if the |
* emergency memory block has been emptied by AppGrowZone, or was never allocated |
* in the first place. StripAddress is called because weÕre comparing the master |
* pointer of the emergency memory handle against zero, and the upper byte of |
* master pointers can be non-zero if the machine is booted in 24-bit addressing |
* mode. |
\******************************************************************************/ |
Boolean NoEmergMem() |
{ |
/* Empty handle means no emergency memory */ |
return (gEmergMem == nil) || (StripAddress( *gEmergMem ) == nil); |
} |
/******************************************************************************\ |
* Public: NewHandleMargin |
* |
* I donÕt call SysError with an ID 25 if there isnÕt enough memory to satisfy |
* the request, so there isnÕt much reason to use the grow-zone proc. So, I |
* deinstall the grow-zone proc temporarily just before I allocate the memory. |
\******************************************************************************/ |
Handle NewHandleMargin( |
Size requestedSize, /* Number of bytes requested to be allocated */ |
Boolean appHeapAlloc, /* Allocate in app heap or system heap? */ |
Boolean clearMem) /* Clear allocated memory or leave it alone? */ |
{ |
Handle aHandle; /* Handle to newly-allocated memory */ |
if (FailLowMemory( requestedSize )) |
aHandle = nil; |
else |
{ |
/* We handle memFullErr properly, so donÕt need grow-zone proc */ |
DeinstallAppGZ(); |
/* Allocate the memory with the requested options */ |
if (!appHeapAlloc && clearMem) |
aHandle = NewHandleSysClear( requestedSize ); |
else if (!appHeapAlloc) |
aHandle = NewHandleSys( requestedSize ); |
else if (clearMem) |
aHandle = NewHandleClear( requestedSize ); |
else |
aHandle = NewHandle( requestedSize ); |
/* Install the grow-zone proc again */ |
InstallAppGZ(); |
} |
return aHandle; |
} |
/******************************************************************************\ |
* Public: NewPtrMargin |
* |
* I donÕt call SysError with an ID 25 if there isnÕt enough memory to satisfy |
* the request, so there isnÕt much reason to use the grow-zone proc. So, I |
* disconnect the grow-zone proc temporarily just before I allocate the memory. |
\******************************************************************************/ |
Ptr NewPtrMargin( |
Size requestedSize, /* Number of bytes requested to be allocated */ |
Boolean appHeapAlloc, /* Allocate in app heap or system heap? */ |
Boolean clearMem) /* Clear allocated memory or leave it alone? */ |
{ |
Ptr aPtr; /* Pointer to newly-allocated memory */ |
if (FailLowMemory( requestedSize )) |
aPtr = nil; |
else |
{ |
/* We handle memFullErr properly, so donÕt need grow-zone proc */ |
DeinstallAppGZ(); |
/* Allocate the memory with the requested options */ |
if (!appHeapAlloc && clearMem) |
aPtr = NewPtrSysClear( requestedSize ); |
else if (! appHeapAlloc) |
aPtr = NewPtrSys( requestedSize ); |
else if (clearMem) |
aPtr = NewPtrClear( requestedSize ); |
else |
aPtr = NewPtr( requestedSize ); |
/* Connect up the grow-zone proc again */ |
InstallAppGZ(); |
} |
return aPtr; |
} |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-03-12