sys7.1-doc-wip/ProcessMgr/Memory.c

550 lines
16 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
File: Memory.c
Contains: Routines manage the Process Mgr's memory resources.
Written by: Erich Ringewald and his bestest buddy Philip York Goldman.
Copyright: © 1986-1992 by Apple Computer, Inc., all rights reserved.
Change History (most recent first):
<12> 10/28/92 DTY Use new Get/Set macros to access ExpandMem.
<11> 3/11/92 DTY 7 Ups patch to GMBlk doesnt respect the slop space in the
system heap, so that the death notice session record will always
be allocated. We do and we need to stop.
<10> 11/22/91 DTY Check allocateGMBlkInSysHeap in GMBlk before trying to allocate
the block in the Process Manager heap. This is one of the fixes
from 7-Up.
<9> 4/5/91 JWM DtY,#86193: GMBlk was fragmenting the system heap and did not
honor emMinSysHeapFreeSpace. Added a ReserveMemSys call to help
with the fragmentation problem. Added a check for leaving a
minimum amount of space in the system heap.
<8> 2/25/91 dba pm: set up A5 in SysZoneFloatSizes
<7> 2/21/91 DFH dba,#82504, #82681, #83168, #83182, #83207: Temp mem heap calls
are more accurate, now that we crawl the system heap to
calculate the high free memory.
<6> 2/9/91 JWM DFH,#79583:Restart and Shutdown don't work in low memory
conditions. A new routine was defined -- GMBlk. It is modeled
after ProcessMgrHiNewHandle but checks the system heap if
ProcessMgrHiNewHandle returns a null handle.
<5> 1/14/91 DFH (dba) Make system mode per process.
<3> 11/5/90 DFH Correct names for TEMPTOPMEM and TEMPDISPOSEHANDLE.
<0> 1/23/87 PYG New Today.
*/
#include <types.h>
#include <errors.h>
#include <memory.h>
#include "Glue.h"
#include "Lomem.h"
#include "Data.h"
#include "Zone.h"
#include "HList.h"
#ifdef MORE_SEGMENTS
#pragma segment kernel_segment
#endif MORE_SEGMENTS
/* The following routines implement selected Memory Manager functions
* for the Process Mgr Zone.
*
* NOTE: Originally, the handle manipulation routines really did something
* different from their "normal" counterparts. Now, only the allocation
* routine (new handle) is special, and the normal call set has been
* made to work with temp blocks.
*/
/* c_TempMaxMem. This routine tries to calculate the size of the largest block we
* could allocate in the Process Mgr zone. Unfortunately, it is inaccurate
* because it does not determine how much of the SYSHEAP space can actually be
* used (it assumes all free/purgeable space is at the top).
*/
pascal u_size
c_TempMaxMem(u_size *pGrowSize)
{
u_size retval;
unsigned long olda5;
olda5 = ProcessMgrA5SimpleSetup();
retval = ProcessMgrMaxBlock();
/* Spec sez this is always zero if heap is not APPLZONE */
if (pGrowSize != nil)
*pGrowSize = 0;
A5SimpleRestore(olda5);
return(retval);
}
/* c_TempFreeMem. Potential free memory for Process Mgr heap also includes memory from
* the top of the system heap. Fairly useless, really.
* NOTE: Assumes that FreeMem can not cause ProcessMgrZone to move, in that it restores
* THEZONE unconditionally, instead of using SafeSetZone and SafeRestoreZone.
*/
pascal u_size
c_TempFreeMem(void)
{
u_size retval, lowPM;
THz theZone;
unsigned long olda5;
olda5 = ProcessMgrA5SimpleSetup();
theZone = THEZONE;
THEZONE = ProcessMgrZone;
/* Initially, calculate space as straddle block (top of system heap and bottom of
* Process Mgr heap) plus the free memory in the Process Mgr heap. This is too much,
* since the bottom space in the Process Mgr heap is counted twice, so we finish by
* subtracting that much off. StraddleBlockSize returns 0 if the amount available
* in the straddle is less than our threshold, but lowPM can still be non-zero.
* NOTE: lowPM can be greater than FreeMem(), since the former includes purgeable
* space but the latter does not. This is why we are careful when subtracting.
*/
if ((retval = StraddleBlockSize(&lowPM, nil, nil, nil) + FreeMem()) > lowPM)
retval -= lowPM;
else
retval = 0;
THEZONE = theZone;
A5SimpleRestore(olda5);
return(retval);
}
/* c_TempTopMem. Return the value of MEMTOP we saw at startup. This call is
* obsolete in Process Mgr 7.0 and later, because Gestalt is better.
*/
pascal Ptr
c_TempTopMem(void)
{
unsigned long olda5;
Ptr retval;
olda5 = ProcessMgrA5SimpleSetup();
retval = initMemTop;
A5SimpleRestore(olda5);
return(retval);
}
/* c_SysZoneFloatSizes. Find the sizes of the blocks that form the straddle block
* across the top of the system heap and bottom of the Temp Memory heap. This is
* the area where the bottom of the Temp Memory heap can float when allocations from
* one heap require abducting memory from the other heap.
*/
pascal OSErr
c_SysZoneFloatSizes(unsigned long *hiSysRoom, unsigned long *lowTempRoom)
{
/* Set up A5 so that we can get at Process Mgr. globals */
unsigned long savedA5 = ProcessMgrA5SimpleSetup();
/* Find space available at top of system zone */
if (hiSysRoom != nil)
*hiSysRoom = GetSystemHiFreeBytes();
/* Find space available at bottom of Process Mgr (aka Temp Memory) zone */
if (lowTempRoom != nil)
*lowTempRoom = GetProcessMgrLoFreeBytes(nil, nil);
/* For now, no error is possible */
A5SimpleRestore(savedA5);
return(noErr);
}
/* GMBlk. Attempts to allocate a handle high in the ProcessMgrZone. If there is no
* memory in the ProcessMgrZone, check the system zone for the space requested.
* GMBlk pays attention to emMinSysHeapFreeSpace (denotes how much free we should
* keep in the system zone.) If there is space in sysZone we call ReserveMemSys
* (forces next block allocated to be low in the system heap), then NewHandleSys
* for the memory. NOTE: FreeMemSys() - logicalSize is conservative. The ReserveMemSys
* will rearrange the heap.
* GMBlk is called by some puppet string routines, some routines in AppleEventExtensions
* and most memory allocation paths in ePPC. Most of the memory will be locked due to being
* accessed from interrupt level by the PPCToolbox.
*/
Handle
GMBlk(u_size logicalSize, OSErr *error)
{
Handle aHdl;
// <11> If allocateGMBlkInSysHeap is set, it means that an application is quitting, and were trying to allocate
// a session record for the appDiedEvent. In this case, always create this session record, even if it means
// taking the system heap down below the gauranteed minimum free space. To save some
// code, were going to do this with a skanky goto. This saves two tests of allocateGMBlkInSysHeap.
if (allocateGMBlkInSysHeap) // <11>
goto allocateBlock; // <11>
aHdl = ProcessMgrHiNewHandle(logicalSize, nil);
if ((*error = MEMERROR) != noErr)
{
if ((FreeMemSys() - logicalSize) > GetExpandMemMinSysHeapFreeSpace())
{
allocateBlock:
ReserveMemSys(logicalSize);
if ((*error = MEMERROR) == noErr)
{
aHdl = NewHandleSys(logicalSize);
*error = MEMERROR;
}
}
else
*error = memFullErr;
}
return(aHdl);
}
/* ProcessMgrNewHandle. Allocate a handle in the ProcessMgrZone.
* Assumes A5 == PROCESSMGRGLOBALS
*/
Handle
ProcessMgrNewHandle(u_size logicalSize)
{
Handle retVal;
THz theZone;
SafeSetZone(theZone, ProcessMgrZone);
retVal = NewHandle(logicalSize);
SafeRestoreZone(theZone);
/* Be kind to the system heap and make sure we're not taking too much. */
ReclaimSpaceIfSysHeapLow(&retVal);
return(retVal);
}
/* c_TempNewHandle. Allocates a relocateable block from our zone for general use
* by an application. We record the allocation so all such can be deallocated when
* the application terminates. Allocates the block high in case the caller decides
* to lock it without moving it high.
*/
pascal Handle
c_TempNewHandle(u_size logicalSize, OSErr *pResultCode)
{
Handle retVal;
HListHdl hList;
unsigned long olda5;
olda5 = ProcessMgrA5SimpleSetup();
assert((pCurrentProcess->p_pcb != nil) && ((*pCurrentProcess->p_pcb)->tempMemHListHdl != nil));
retVal = ProcessMgrHiNewHandle(logicalSize, nil);
if ((*pResultCode = MEMERROR) == noErr)
{
hList = (pCurrentProcess->p_systemModeLevel == 0) ? (*pCurrentProcess->p_pcb)->tempMemHListHdl : SystemTempMemHList;
if (AddToHList(hList,(HListElem)retVal) != noErr)
{
*pResultCode = MEMERROR;
DisposHandle(retVal);
retVal = nil;
}
}
A5SimpleRestore(olda5);
return(retVal);
}
/* LikelyTempMemHList. Before we leap right into a semi-expensive scan of a guy's
* temp memory lists, we can make couple of cheap checks to eliminate values that
* can not be found. First, if the value is lower than the beginning of the Process Mgr
* zone, it is out of bounds (like nil or in the system heap). Second, we make sure
* the value is not within the application partition (this works even if its from a
* zone-within-a-zone in the application).
* IMPORTANT: Assumes the tElem has already been stripped, if necessary.
* NOTE: The elimination checks should stay fairly modest, because many processes
* won't have any temporary memory at all, meaning the list search is cheap anyway.
* NOTE: Assumes a5 == PROCESSMGRGLOBALS
*/
Boolean
LikelyTempMemHList(HListElem tElem, HListHdl *hList)
{
Boolean retVal;
PEntryPtr theProc;
retVal = false;
theProc = pCurrentProcess;
if ( ((Ptr) tElem > ProcessMgrZone)
&& (theProc != nil)
&& (((Ptr)tElem < theProc->p_zone)
|| ((Ptr)tElem >= ((Ptr)theProc->p_zone + theProc->p_size))) )
{
*hList = (*theProc->p_pcb)->tempMemHListHdl;
retVal = true;
}
return(retVal);
}
/* InCurrTempMem. Takes a potential HListElem from the temporary memory allocations
* of the current process, and verifies it. Returns an OSErr if we couldn't spot it.
* NOTE: Assumes a5 == PROCESSMGRGLOBALS
*/
OSErr
InCurrTempMem(HListElem hElem)
{
OSErr retval=qErr;
HListHdl hList;
hElem = (HListElem) StripAddress((Ptr) hElem);
if (LikelyTempMemHList(hElem,&hList))
{
if (HListExists(hList))
retval = InHList(hList,hElem);
if (retval != noErr)
retval = InHList(SystemTempMemHList, hElem);
}
return(retval);
}
/* RemoveCurrTempMem. Takes a potential HListElem from the temporary memory allocations
* of the current process, and de-registers the allocation. Returns an OSErr if we didn't
* deregister anything.
* NOTE: Assumes a5 == PROCESSMGRGLOBALS
*/
OSErr
RemoveCurrTempMem(HListElem hElem)
{
Boolean retval=qErr;
HListHdl hList;
hElem = (HListElem) StripAddress((Ptr) hElem);
if (LikelyTempMemHList(hElem, &hList))
{
if (HListExists(hList))
retval = RemoveFromHList(hList, hElem);
if (retval != noErr)
retval = RemoveFromHList(SystemTempMemHList, hElem);
}
return retval;
}
/* RecoverCurrTempHdl. Takes a potential master pointer of a temporary memory handle, and
* returns the handle. It's similar to RecoverHandle.
* NOTE: Assumes a5 == PROCESSMGRGLOBALS
*/
Handle
RecoverCurrTempHdl(Ptr targetPtr)
{
Handle retVal;
HListHdl hList;
retVal = nil;
targetPtr = StripAddress((Ptr) targetPtr);
if (LikelyTempMemHList((HListElem) targetPtr, &hList))
{
if (HListExists(hList))
retVal = (Handle) RecoverHListElem(hList, targetPtr);
if (retVal == nil)
retVal = (Handle) RecoverHListElem(SystemTempMemHList, targetPtr);
}
return(retVal);
}
/* These have to stay in seg Main since they call glue but don't set up a5 */
#ifdef MORE_SEGMENTS
#pragma segment Main
#endif MORE_SEGMENTS
/* TempHLock. Lock the relocatable block, preventing it from being moved
* within its heap zone.
*/
pascal void
c_TempHLock(Handle hdl, OSErr *pResultCode)
{
HLock(hdl);
*pResultCode = MEMERROR;
}
/* TempHUnLock. UnLock the relocatable block, allowing it to be moved within
* its heap zone.
*/
pascal void
c_TempHUnLock(Handle hdl, OSErr *pResultCode)
{
HUnlock(hdl);
*pResultCode = MEMERROR;
}
/* TempDisposeHandle. Release the memory occupied by the relocatable block whose
* handle is h.
*/
pascal void
c_TempDisposeHandle(Handle hdl, OSErr *pResultCode)
{
DisposHandle(hdl);
*pResultCode = MEMERROR;
}
#ifdef MM_DEBUG
/* Check for entries in the system resource map that have MPs in an application heap.
* The check is enabled by holding down the command key (by itself).
*/
Boolean
ToggleDoCheck(Boolean alreadyChecking)
{
EventRecord doCheckEventRec;
Boolean doToggle;
#define noEventMask 0
#define CHECK_SKIP_MODIFIERS (cmdKey+shiftKey+optionKey)
#define DONT_SKIP_MODIFIERS (cmdKey+optionKey)
(void)OSEventAvail(noEventMask, &doCheckEventRec);
if ( (doCheckEventRec.modifiers & CHECK_SKIP_MODIFIERS) == DONT_SKIP_MODIFIERS )
{
if (alreadyChecking = !alreadyChecking)
dbmsg("Turning on sys map and MP checking...");
else
dbmsg("Turning off sys map and MP checking...");
}
return alreadyChecking;
}
static Boolean doCheck = true;
static Boolean isSafeNow = false;
void
CheckSysMapForBadMPs(void)
{
short *pTypeList;
TypeEntry *pCurType;
RefEntry *pCurRef;
short cTypes, cResources;
u_long sysLimit;
isSafeNow = true;
if ((doCheck = ToggleDoCheck(doCheck)) == false)
return;
pTypeList = *SYSMAPHANDLE + 24;
pTypeList = *SYSMAPHANDLE + *pTypeList;
pCurType = pTypeList + 1;
cTypes = *pTypeList;
sysLimit = StripAddress((Ptr) SYSZONE->bkLim);
while (cTypes >= 0)
{
pCurRef = (Ptr)pTypeList + pCurType->refListOffset;
cResources = pCurType->cResources;
while (cResources >= 0)
{
if ((StripAddress((Ptr) pCurRef) > sysLimit)
dbmsg("Bad Resource!");
pCurRef++;
--cResources;
}
pCurType++;
--cTypes;
}
}
#define IS_HANDLE_IN_ZONE(theHdl, theZone) (((Ptr)(theHdl) > (Ptr)(theZone)) && ((Ptr)(theHdl) < (theZone)->bkLim))
/* Check that all (free) MPs in this zone fall within this zone */
void
CheckZoneForBadFreeMPs(THz theZone)
{
Ptr *pMP;
if (!doCheck)
return;
theZone = StripAddress((Ptr) theZone);
for (pMP = theZone->hFstFree; pMP != nil; pMP = (Ptr *)*pMP)
{
pMP = StripAddress((Ptr) pMP);
if (!(IS_HANDLE_IN_ZONE(pMP, theZone)))
{
dbmsg("Bad free MP list in given zone");
}
}
}
/* Return the correct zone for the purged handle.
* A5 world needed: PROCESSMGRGLOBALS
*/
THz
GetCorrectZone(Handle purgedHdl)
{
PEntryPtr pProc;
THz theZone;
if ((Ptr)purgedHdl < SYSZONE || (Ptr)purgedHdl > ProcessMgrZone->bkLim)
return nil; /* Out of all bounds */
if ((Ptr)purgedHdl < SYSZONE->bkLim)
return SYSZONE;
pProc = pProcessList;
while (pProc != nil)
{
assert(IsProcessEntry(pProc));
theZone = (THz)(StripAddess(pProc->p_zone));
if (IS_HANDLE_IN_ZONE(purgedHdl, theZone))
return theZone;
pProc = pProc->p_NextProcess;
}
/* Couldn't find in any of the app zones so must be in Process Mgr zone */
return ProcessMgrZone;
}
/* Check if given handle is "bad" */
void
CheckBadHandle(Handle hdl)
{
unsigned long olda5;
THz correctZone, GetCorrectZone(Handle);
THz pZone = &correctZone;
olda5 = ProcessMgrA5SimpleSetup();
hdl = (Handle)(StripAddress((Ptr) hdl)); /* Strip! */
if (isSafeNow && doCheck)
{
if (hdl == nil)
{
dbmsg("Operating on nil handle");
}
else if (*hdl == nil)
{
/* Purged */
if ((correctZone = GetCorrectZone(hdl)) == nil)
dbmsg("Couldn't find a zone for purged handle");
else if (correctZone != THEZONE)
{
if (THEZONE == ProcessMgrZone)
{
dbmsg("Purged handle op possibly trashing ProcessMgrZone MP list");
}
else if (THEZONE == SYSZONE)
{
dbmsg("Purged handle op possibly trashing sys zone MP list");
}
else if (!(THEZONE == APPLZONE && correctZone == SYSZONE))
{
dbmsg("Bad zone for operation on purged handle");
}
}
}
}
A5SimpleRestore(olda5);
}
#endif MM_DEBUG