mirror of
https://github.com/microsoft/Microsoft-3D-Movie-Maker.git
synced 2024-11-25 03:33:22 +01:00
565 lines
13 KiB
C++
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
|
||
|
|