Microsoft-3D-Movie-Maker/kauai/SRC/MEMMAC.CPP

565 lines
13 KiB
C++

/* Copyright (c) Microsoft Corporation.
Licensed under the MIT License. */
/***************************************************************************
Author: ShonK
Project: Kauai
Reviewed:
Copyright (c) Microsoft Corporation
Macintosh memory management.
***************************************************************************/
#include "util.h"
ASSERTNAME
// REVIEW shonk: Mac: implement HQ statistics
// HQ header - actually goes at the end of the hq.
struct HQH
{
#ifdef DEBUG
short swMagic; // to detect memory trashing
short cactRef; // for marking memory
schar *pszsFile; // source file that allocation request is coming from
long lwLine; // line in file that allocation request is coming from
HQ hqPrev; // previous hq in doubly linked list
HQ hqNext; // next hq in doubly linked list
#endif //DEBUG
byte cactLock; // lock count
byte cbExtra; // count of extra bytes
};
#ifdef DEBUG
HQ _hqFirst; // head of the doubly linked list
long vcactSuspendCheckPointers = 0;
#endif //DEBUG
inline void *_QvFromHq(HQ hq)
{ return vadst.PvStrip(*(void **)hq); }
inline HQH *_QhqhFromHq(HQ hq)
{
return (HQH *)PvAddBv(vadst.PvStrip(*(void **)hq),
GetHandleSize((HN)hq) - size(HQH));
}
inline HQH *_QhqhFromHqBv(HQ hq, long bv)
{ return (HQH *)PvAddBv(vadst.PvStrip(*(void **)hq), bv); }
long __pascal _CbFreeStuff(long cb);
ADST vadst;
/***************************************************************************
This is the GrowZone proc (a call back from the Mac OS memory manager).
If _fInAlloc is true, we don't do anything, cuz the code that's doing
the allocation will free stuff and try again.
If _fInAlloc is false, it probably means that the Toolbox is allocating
something, in which case, the priority is critical.
REVIEW shonk: should we just free an emergency buffer and return?
***************************************************************************/
long __pascal _CbFreeStuff(long cb)
{
if (_fInAlloc || pvNil == vpfnlib)
return 0;
_fInAlloc = fTrue;
cb = (*vpfnlib)(cb, mprCritical);
_fInAlloc = fFalse;
return cb;
}
/***************************************************************************
Constructor for the address stripper - overloaded to also set the
grow-zone proc.
***************************************************************************/
ADST::ADST(void)
{
_lwMaskAddress = (long)StripAddress((void *)0xFFFFFFFFL);
SetGrowZone(&_CbFreeStuff);
}
/***************************************************************************
Allocates a new moveable block.
***************************************************************************/
#ifdef DEBUG
bool FAllocHqDebug(HQ *phq, long cb, ulong grfmem, long mpr,
schar *pszsFile, long lwLine)
#else //!DEBUG
bool FAllocHq(HQ *phq, long cb, ulong grfmem, long mpr)
#endif //!DEBUG
{
AssertVarMem(phq);
AssertIn(cb, 0, kcbMax);
HQH hqh;
if (_fInAlloc)
{
Bug("recursing in FAllocHq");
goto LFail;
}
#ifdef DEBUG
if (_hqFirst != hqNil)
AssertHq(_hqFirst);
#endif //DEBUG
if (cb > kcbMax)
{
BugVar("who's allocating a humongous block?", &cb);
goto LFail;
}
hqh.cbExtra = (-cb) & 3;
Assert((cb + hqh.cbExtra) % 4 == 0, 0);
// assert we don't overflow (the limit of kcbMax should ensure this)
Assert(cb + size(HQH) + hqh.cbExtra > cb, 0);
cb += hqh.cbExtra + size(HQH);
_fInAlloc = fTrue;
do
{
*phq = (grfmem & fmemClear) ? NewHandleClear(cb) : NewHandle(cb);
}
while (hqNil == *phq && vpfnlib != pvNil && (*vpfnlib)(cb, mpr) > 0);
_fInAlloc = fFalse;
if (hqNil == *phq)
{
LFail:
*phq = hqNil;
PushErc(ercOomHq);
return fFalse;
}
#ifdef DEBUG
if (!(grfmem & fmemClear))
FillPb(_QvFromHq(*phq), cb, kbGarbage);
hqh.swMagic = kswMagicMem;
hqh.pszsFile = pszsFile;
hqh.lwLine = lwLine;
hqh.hqPrev = hqNil;
hqh.hqNext = _hqFirst;
if (_hqFirst != hqNil)
{
Assert(_QhqhFromHq(_hqFirst)->hqPrev == hqNil,
"_hqFirst's prev is not nil");
_QhqhFromHq(_hqFirst)->hqPrev = *phq;
}
_hqFirst = *phq;
#endif //DEBUG
hqh.cactLock = 0;
*_QhqhFromHqBv(*phq, cb - size(HQH)) = hqh;
AssertHq(*phq);
return fTrue;
}
/***************************************************************************
Resizes the given hq. *phq may change (on Windows). If fhqClear,
clears any newly added space.
***************************************************************************/
bool FResizePhq(HQ *phq, long cb, ulong grfmem, long mpr)
{
AssertVarMem(phq);
AssertHq(*phq);
AssertIn(cb, 0, kcbMax);
HQH hqh;
long cbOld;
bool fInAllocSave;
short err;
if (cb > kcbMax)
{
BugVar("who's resizing a humongous block?", &cb);
goto LFail;
}
cbOld = GetHandleSize((HN)*phq) - size(HQH);
if (_fInAlloc && cb > cbOld)
{
Bug("recursing in FResizePhq");
goto LFail;
}
hqh = *_QhqhFromHqBv(*phq, cbOld);
if (hqh.cactLock != 0)
{
Bug("Resizing locked HQ");
goto LFail;
}
#ifdef DEBUG
// trash the old stuff
if (cbOld > cb)
FillPb(PvAddBv(_QvFromHq(*phq), cb), cbOld + size(HQH) - cb, kbGarbage);
#endif
cbOld -= hqh.cbExtra;
hqh.cbExtra = (-cb) & 3;
Assert((cb + hqh.cbExtra) % 4 == 0, 0);
// assert we don't overflow (the limit of kcbMax should ensure this)
Assert(cb + size(HQH) + hqh.cbExtra > cb, 0);
fInAllocSave = _fInAlloc;
_fInAlloc = fTrue;
do
{
err = ErrSetHandleSize((HN)*phq, cb + hqh.cbExtra + size(HQH));
}
while (err != noErr && cb > cbOld && vpfnlib != pvNil &&
(*vpfnlib)(cb - cbOld, mpr) > 0);
_fInAlloc = fInAllocSave;
if (err != noErr)
{
LFail:
AssertHq(*phq);
PushErc(ercOomHq);
return fFalse;
}
if (cbOld < cb)
{
if (grfmem & fmemClear)
ClearPb(PvAddBv(_QvFromHq(*phq), cbOld), cb - cbOld);
#ifdef DEBUG
else // trash the new stuff
FillPb(PvAddBv(_QvFromHq(*phq), cbOld), cb - cbOld, kbGarbage);
// trash the rest of the block
FillPb(PvAddBv(_QvFromHq(*phq), cb),
hqh.cbExtra + size(HQH), kbGarbage);
#endif //DEBUG
}
// put the HQH where it belongs
*_QhqhFromHqBv(*phq, cb + hqh.cbExtra) = hqh;
AssertHq(*phq);
return fTrue;
}
/***************************************************************************
If hq is not nil, frees it.
***************************************************************************/
void FreePhq(HQ *phq)
{
AssertVarMem(phq);
if (*phq == hqNil)
return;
#ifdef DEBUG
AssertHq(*phq);
HQH hqh;
hqh = *_QhqhFromHq(*phq);
Assert(hqh.cactLock == 0, "Freeing locked HQ");
// update prev's next pointer
if (hqh.hqPrev == hqNil)
{
Assert(_hqFirst == *phq, "prev is wrongly nil");
_hqFirst = hqh.hqNext;
}
else
{
AssertHq(hqh.hqPrev);
Assert(_hqFirst != *phq, "prev should be nil");
_QhqhFromHq(hqh.hqPrev)->hqNext = hqh.hqNext;
}
// update next's prev pointer
if (hqh.hqNext != hqNil)
{
AssertHq(hqh.hqNext);
_QhqhFromHq(hqh.hqNext)->hqPrev = hqh.hqPrev;
}
// fill the block with garbage
FillPb(_QvFromHq(*phq), GetHandleSize((HN)*phq), kbGarbage);
#endif //DEBUG
DisposHandle((HN)*phq);
*phq = hqNil;
}
/***************************************************************************
Return the size of the hq (the client area of the block).
***************************************************************************/
long CbOfHq(HQ hq)
{
AssertHq(hq);
long cbRaw;
cbRaw = GetHandleSize((HN)hq) - size(HQH);
return cbRaw - _QhqhFromHqBv(hq, cbRaw)->cbExtra;
}
/***************************************************************************
Copy an hq to a new block.
***************************************************************************/
bool FCopyHq(HQ hqSrc, HQ *phqDst, long mpr)
{
AssertHq(hqSrc);
AssertVarMem(phqDst);
HQH *qhqh;
short err;
if (_fInAlloc)
{
Bug("recursing in FCopyHq");
goto LFail;
}
*phqDst = hqSrc;
_fInAlloc = fTrue;
do
{
err = HandToHand((HN *)phqDst);
}
while (err != noErr && vpfnlib != pvNil && (*vpfnlib)(CbOfHq(hqSrc), mpr) > 0);
_fInAlloc = fFalse;
if (err != noErr)
{
LFail:
*phqDst = hqNil;
PushErc(ercOomHq);
return fFalse;
}
qhqh = _QhqhFromHq(*phqDst);
#ifdef DEBUG
qhqh->swMagic = kswMagicMem;
qhqh->pszsFile = __szsFile;
qhqh->lwLine = __LINE__;
qhqh->hqPrev = hqNil;
qhqh->hqNext = _hqFirst;
if (_hqFirst != hqNil)
{
Assert(_QhqhFromHq(_hqFirst)->hqPrev == hqNil,
"_hqFirst's prev is not nil");
_QhqhFromHq(_hqFirst)->hqPrev = *phqDst;
}
_hqFirst = *phqDst;
#endif //DEBUG
qhqh->cactLock = 0;
AssertHq(*phqDst);
return fTrue;
}
#ifdef DEBUG
/***************************************************************************
Returns a volatile pointer from an hq.
***************************************************************************/
void *QvFromHq(HQ hq)
{
AssertHq(hq);
return _QvFromHq(hq);
}
#endif //DEBUG
/***************************************************************************
Lock the hq and return a pointer to the data.
***************************************************************************/
void *PvLockHq(HQ hq)
{
AssertHq(hq);
HQH *qhqh;
qhqh = _QhqhFromHq(hq);
if (qhqh->cactLock++ == 0)
HLock((HN)hq);
Assert(_QhqhFromHq(hq)->cactLock > 0, "overflow in cactLock");
return _QvFromHq(hq);
}
/***************************************************************************
Unlock the hq. Asserts and does nothing if the lock count is zero.
***************************************************************************/
void UnlockHq(HQ hq)
{
AssertHq(hq);
HQH *qhqh;
qhqh = _QhqhFromHq(hq);
Assert(qhqh->cactLock > 0, "hq not locked");
if (qhqh->cactLock > 0)
{
if (--qhqh->cactLock == 0)
HUnlock((HN)hq);
}
}
#ifdef DEBUG
/***************************************************************************
Assert that a given hq is valid.
***************************************************************************/
void AssertHq(HQ hq)
{
static void *_pvMinZone;
void *pvLimZone;
void *qv;
HQH hqh;
long cb;
short sw;
// make sure hq isn't nil or odd
if (hq == hqNil || (long(hq) & 1) != 0)
{
BugVar("hq is nil or odd", &hq);
return;
}
// make sure *hq is not nil or odd
if ((qv = _QvFromHq(hq)) == pvNil || (long(qv) & 1) != 0)
{
BugVar("*hq is nil or odd", &qv);
return;
}
// get the heap limits and make sure the hq is in it
if (pvNil == _pvMinZone)
_pvMinZone = ApplicZone();
pvLimZone = (void *)LMGetHeapEnd();
if (hq <= _pvMinZone || hq >= pvLimZone)
{
BugVar("hq not in application heap", &hq);
return;
}
if ((qv = _QvFromHq(hq)) <= _pvMinZone || qv >= pvLimZone)
{
BugVar("*hq not in the appication heap", &qv);
return;
}
cb = GetHandleSize((HN)hq);
AssertVar(cb >= size(HQH), "hq block is too small", &cb);
AssertVar(cb <= BvSubPvs(pvLimZone, _QvFromHq(hq)),
"hq block runs past end of heap", &cb);
// verify the HQH
hqh = *_QhqhFromHqBv(hq, cb - size(HQH));
if (hqh.swMagic != kswMagicMem)
{
BugVar("end of hq block is trashed", &hqh);
return;
}
AssertVar(cb >= size(HQH) + hqh.cbExtra,
"cbExtra or cb is wrong", &cb);
sw = HGetState((HN)hq);
Assert((hqh.cactLock == 0) == !(sw & 0x0080), "lock count is wrong");
Assert((hqh.hqPrev == hqNil) == (hq == _hqFirst), "hqPrev is wrong");
// verify the links
if (hqh.hqPrev != hqNil)
Assert(_QhqhFromHq(hqh.hqPrev)->hqNext == hq, "hqNext in prev is wrong");
if (hqh.hqNext != hqNil)
Assert(_QhqhFromHq(hqh.hqNext)->hqPrev == hq, "hqPrev in next is wrong");
}
/***************************************************************************
Increment the ref count on an hq.
***************************************************************************/
void MarkHq(HQ hq)
{
if (hq != hqNil)
{
AssertHq(hq);
_QhqhFromHq(hq)->cactRef++;
}
}
/***************************************************************************
Asserts on all unmarked HQs.
***************************************************************************/
void _AssertUnmarkedHqs(void)
{
HQ hq;
for (hq = _hqFirst; hq != hqNil; hq = _QhqhFromHq(hq)->hqNext)
{
AssertHq(hq);
if (_QhqhFromHq(hq)->cactRef == 0)
{
HQH hqh;
long cb;
achar stz[kcbMaxStz];
cb = CbOfHq(hq);
hqh = *_QhqhFromHq(hq);
FFormatStzSz(stz,
"Lost hq of size %d allocated at line %d of file '%z'",
cb, hqh.lwLine, hqh.pszsFile);
Bug(PszStz(stz));
}
}
}
/***************************************************************************
Clears all reference counts.
***************************************************************************/
void _UnmarkAllHqs(void)
{
HQ hq;
for (hq = _hqFirst; hq != hqNil; hq = _QhqhFromHq(hq)->hqNext)
{
AssertHq(hq);
_QhqhFromHq(hq)->cactRef = 0;
}
}
/***************************************************************************
Assert on obviously bogus pointers. Assert that [pv, pv+cb) resides
in either the app zone or the system zone. If cb is zero, pv can
be anything (including nil).
***************************************************************************/
void AssertPvCb(void *pv, long cb)
{
static long _lwStrip;
static void *_pvMinZone, *_pvMinSysZone;
void *pvLimZone, *pvLimSysZone;
void *pvClean, *pvLim;
if (vcactSuspendCheckPointers != 0 || cb == 0)
return;
if (_pvMinZone == 0)
{
_pvMinZone = ApplicZone();
_pvMinSysZone = SystemZone();
_lwStrip = (long)StripAddress((void *)-1L);
}
pvLimZone = (void *)LMGetCurrentA5();
pvLimSysZone = (void *)(*(long *)_pvMinSysZone & _lwStrip);
pvClean = (void *)((long)pv & _lwStrip);
pvLim = PvAddBv(pvClean, cb);
if (pvClean < _pvMinZone || pvLim > pvLimZone)
{
AssertVar(pvClean >= _pvMinSysZone && pvLim <= pvLimSysZone,
"(pv,cb) not in app or sys zone", &pv);
}
}
#endif //DEBUG