mirror of
https://github.com/microsoft/Microsoft-3D-Movie-Maker.git
synced 2024-11-22 02:12:33 +01:00
620 lines
15 KiB
C++
620 lines
15 KiB
C++
/* Copyright (c) Microsoft Corporation.
|
|
Licensed under the MIT License. */
|
|
|
|
/***************************************************************************
|
|
Author: ShonK
|
|
Project: Kauai
|
|
Reviewed:
|
|
Copyright (c) Microsoft Corporation
|
|
|
|
Base classes. Base class that all other classes are derived from and
|
|
a base linked list class for implementing singly linked lists.
|
|
|
|
***************************************************************************/
|
|
#include "util.h"
|
|
ASSERTNAME
|
|
|
|
|
|
#ifdef DEBUG
|
|
long vcactSuspendAssertValid = 0;
|
|
long vcactAVSave = 0;
|
|
long vcactAV = kswMax;
|
|
|
|
|
|
/*****************************************************************************
|
|
Debugging stuff to track leaked allocated objects.
|
|
******************************************************************************/
|
|
// Debugging object info.
|
|
const long kclwStackDoi = 10;
|
|
struct DOI
|
|
{
|
|
short swMagic; // magic number == kswMagicMem
|
|
short cactRef; // for marking memory and asserting on unused objects
|
|
PSZS pszsFile; // file NewObj appears in
|
|
long lwLine; // line NewObj appears on
|
|
long cbTot; // total size of the block, including the DOI
|
|
long lwThread; // thread that allocated this
|
|
long rglwStack[10]; // what we get from following the EBP/A6 chain
|
|
DOI *pdoiNext; // singly linked list
|
|
DOI **ppdoiPrev;
|
|
};
|
|
|
|
#define kcbBaseDebug size(DOI)
|
|
|
|
priv void _AssertDoi(DOI *pdoi, bool tLinked);
|
|
inline DOI *_PdoiFromBase(void *pv)
|
|
{ return (DOI *)PvSubBv(pv, kcbBaseDebug); }
|
|
inline BASE *_PbaseFromDoi(DOI *pdoi)
|
|
{ return (BASE *)PvAddBv(pdoi, kcbBaseDebug); }
|
|
priv void _LinkDoi(DOI *pdoi, DOI **ppdoiFirst);
|
|
priv void _UnlinkDoi(DOI *pdoi);
|
|
|
|
DOI *_pdoiFirst; // Head of linked list of all allocated objects.
|
|
DOI *_pdoiFirstRaw; // Head of linked list of raw newly allocated objects.
|
|
|
|
inline void _Enter(void)
|
|
{ vmutxBase.Enter(); }
|
|
inline void _Leave(void)
|
|
{ vmutxBase.Leave(); }
|
|
|
|
#define klwMagicAllocatedBase 'ALOC'
|
|
#define klwMagicNonAllocBase 'NOAL'
|
|
|
|
#else //!DEBUG
|
|
#define kcbBaseDebug 0
|
|
#endif //!DEBUG
|
|
|
|
|
|
RTCLASS(BLL)
|
|
|
|
/***************************************************************************
|
|
Returns the run-time class id (cls) of the class.
|
|
***************************************************************************/
|
|
long BASE::Cls(void)
|
|
{
|
|
return kclsBASE;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Returns true iff cls is kclsBASE.
|
|
***************************************************************************/
|
|
bool BASE::FIs(long cls)
|
|
{
|
|
return kclsBASE == cls;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Static method. Returns true iff cls is kclsBASE.
|
|
***************************************************************************/
|
|
bool BASE::FWouldBe(long cls)
|
|
{
|
|
return kclsBASE == cls;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Constructor for a BASE object. Sets _cactRef to 1 and in debug sets
|
|
the magic number to indicate whether the thing was allocated.
|
|
***************************************************************************/
|
|
BASE::BASE(void)
|
|
{
|
|
_cactRef = 1;
|
|
#ifdef DEBUG
|
|
DOI *pdoi = _pdoiFirstRaw;
|
|
|
|
if (pvNil != pdoi)
|
|
{
|
|
// Note that we have to check _pdoiFirstRaw before entering the
|
|
// mutx so we don't try to enter the mutx before it's been
|
|
// initialized (during global object construction).
|
|
_Enter();
|
|
|
|
// see if this is in the raw list - note that we have to refresh
|
|
// _pdoiFirstRaw in case another thread grabbed it before we got
|
|
// the critical section
|
|
for (pdoi = _pdoiFirstRaw;
|
|
pdoi != pvNil && this != _PbaseFromDoi(pdoi);
|
|
pdoi = pdoi->pdoiNext)
|
|
{
|
|
_AssertDoi(pdoi, tYes);
|
|
}
|
|
|
|
if (pvNil != pdoi)
|
|
{
|
|
// this is us!
|
|
_UnlinkDoi(pdoi);
|
|
_LinkDoi(pdoi, &_pdoiFirst);
|
|
|
|
_lwMagic = klwMagicAllocatedBase;
|
|
AssertValid(0);
|
|
}
|
|
|
|
_Leave();
|
|
}
|
|
|
|
if (pvNil == pdoi)
|
|
{
|
|
_lwMagic = klwMagicNonAllocBase;
|
|
//don't call AssertValid here, since this may be during
|
|
//global initialization (FAssertProc and/or AssertPvCb may not be
|
|
//callable).
|
|
}
|
|
|
|
#endif //DEBUG
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Increments the reference count.
|
|
***************************************************************************/
|
|
void BASE::AddRef(void)
|
|
{
|
|
AssertThis(0);
|
|
_cactRef++;
|
|
|
|
//NOTE: some classes allow _cactRef == 0, so we can't assert _cactRef > 1
|
|
Assert(_cactRef > 0, "_cactRef not positive");
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Decrement the reference count and delete it if the reference count goes
|
|
to zero.
|
|
***************************************************************************/
|
|
void BASE::Release(void)
|
|
{
|
|
AssertThis(0);
|
|
if (--_cactRef <= 0)
|
|
{
|
|
AssertThis(fobjAllocated);
|
|
delete this;
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Used to allocate all objects. Clears the block and in debug, adds
|
|
magic number, reference count for marking and inserts in linked list
|
|
of allocated objects for object leakage tracking.
|
|
***************************************************************************/
|
|
#ifdef DEBUG
|
|
void *BASE::operator new(size_t cb, PSZS pszsFile, long lwLine)
|
|
#else //!DEBUG
|
|
void *BASE::operator new(size_t cb)
|
|
#endif //!DEBUG
|
|
{
|
|
AssertVarMem(pszsFile);
|
|
void *pv;
|
|
|
|
#ifdef DEBUG
|
|
// do failure simulation
|
|
_Enter();
|
|
|
|
if (vdmglob.dmaglBase.FFail())
|
|
{
|
|
_Leave();
|
|
PushErc(ercOomNew);
|
|
return pvNil;
|
|
}
|
|
|
|
_Leave();
|
|
#endif //DEBUG
|
|
|
|
#ifdef WIN
|
|
if ((pv = (void *)GlobalAlloc(GMEM_FIXED | GMEM_ZEROINIT, cb + kcbBaseDebug)) == pvNil)
|
|
#else //!WIN
|
|
if ((pv = ::operator new(cb + kcbBaseDebug)) == pvNil)
|
|
#endif //!WIN
|
|
PushErc(ercOomNew);
|
|
else
|
|
{
|
|
#ifdef DEBUG
|
|
_Enter();
|
|
|
|
DOI *pdoi = (DOI *)pv;
|
|
|
|
pdoi->swMagic = kswMagicMem;
|
|
pdoi->cactRef = 0;
|
|
pdoi->pszsFile = pszsFile;
|
|
pdoi->lwLine = lwLine;
|
|
pdoi->cbTot = cb + kcbBaseDebug;
|
|
pdoi->lwThread = LwThreadCur();
|
|
pdoi->ppdoiPrev = pvNil;
|
|
_LinkDoi(pdoi, &_pdoiFirstRaw);
|
|
pv = _PbaseFromDoi(pdoi);
|
|
|
|
#ifdef WIN
|
|
// follow the EBP chain....
|
|
long *plw;
|
|
long ilw;
|
|
|
|
__asm { mov plw,ebp }
|
|
for (ilw = 0; ilw < kclwStackDoi; ilw++)
|
|
{
|
|
if (pvNil == plw || IsBadReadPtr(plw, 2 * size(long)) ||
|
|
*plw <= (long)plw)
|
|
{
|
|
pdoi->rglwStack[ilw] = 0;
|
|
plw = pvNil;
|
|
}
|
|
else
|
|
{
|
|
pdoi->rglwStack[ilw] = plw[1];
|
|
plw = (long *)*plw;
|
|
}
|
|
}
|
|
#endif //WIN
|
|
|
|
// update statistics
|
|
vdmglob.dmaglBase.Allocate(cb);
|
|
|
|
_Leave();
|
|
#endif //DEBUG
|
|
#ifndef WIN
|
|
ClearPb(pv, cb);
|
|
#endif //!WIN
|
|
}
|
|
return pv;
|
|
}
|
|
|
|
|
|
#if defined(DEBUG) || defined(WIN)
|
|
/***************************************************************************
|
|
DEBUG : Unlink from linked list of allocated objects and free the memory.
|
|
***************************************************************************/
|
|
void BASE::operator delete(void *pv)
|
|
{
|
|
#ifdef DEBUG
|
|
DOI *pdoi = _PdoiFromBase(pv);
|
|
|
|
_UnlinkDoi(pdoi);
|
|
|
|
// update statistics
|
|
vdmglob.dmaglBase.Free(pdoi->cbTot - kcbBaseDebug);
|
|
|
|
TrashPvCb(pdoi, pdoi->cbTot);
|
|
pv = pdoi;
|
|
#endif // DEBUG
|
|
#ifdef WIN
|
|
GlobalFree((HGLOBAL)pv);
|
|
#else //!WIN
|
|
::delete(pv);
|
|
#endif //!WIN
|
|
}
|
|
#endif //DEBUG || WIN
|
|
|
|
#ifdef DEBUG
|
|
/***************************************************************************
|
|
Assert the this pointer is valid.
|
|
***************************************************************************/
|
|
void BASE::AssertValid(ulong grfobj)
|
|
{
|
|
AssertVarMem(this);
|
|
AssertIn(_cactRef, 0, kcbMax);
|
|
if (_lwMagic != klwMagicAllocatedBase)
|
|
{
|
|
Assert(!(grfobj & fobjAllocated), "should be allocated");
|
|
AssertVar(_lwMagic == klwMagicNonAllocBase,
|
|
"_lwMagic wrong", &_lwMagic);
|
|
AssertPvCb(this, size(BASE));
|
|
return;
|
|
}
|
|
Assert(!(grfobj & fobjNotAllocated), "should not be allocated");
|
|
|
|
_Enter();
|
|
|
|
DOI *pdoi = _PdoiFromBase(this);
|
|
_AssertDoi(pdoi, tYes);
|
|
|
|
_Leave();
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
If this objects has already been marked, just return. If not, call
|
|
its MarkMem method.
|
|
***************************************************************************/
|
|
void BASE::MarkMemStub(void)
|
|
{
|
|
AssertThis(0);
|
|
if (_lwMagic != klwMagicAllocatedBase)
|
|
{
|
|
AssertVar(_lwMagic == klwMagicNonAllocBase,
|
|
"_lwMagic wrong", &_lwMagic);
|
|
MarkMem();
|
|
return;
|
|
}
|
|
|
|
DOI *pdoi = _PdoiFromBase(this);
|
|
if (pdoi->cactRef == 0 && pdoi->lwThread == LwThreadCur())
|
|
MarkMem();
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Mark object.
|
|
***************************************************************************/
|
|
void BASE::MarkMem(void)
|
|
{
|
|
AssertValid(0);
|
|
if (_lwMagic == klwMagicAllocatedBase)
|
|
_PdoiFromBase(this)->cactRef++;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Assert that a doi is valid and optionally linked or unlinked.
|
|
***************************************************************************/
|
|
void _AssertDoi(DOI *pdoi, bool tLinked)
|
|
{
|
|
_Enter();
|
|
|
|
AssertPvCb(pdoi, size(BASE) + kcbBaseDebug);
|
|
AssertIn(pdoi->cbTot, size(BASE) + kcbBaseDebug, kcbMax);
|
|
AssertPvCb(pdoi, pdoi->cbTot);
|
|
|
|
AssertVar(pdoi->swMagic == kswMagicMem, "magic number has been hammered",
|
|
&pdoi->swMagic);
|
|
AssertVar(pdoi->cactRef >= 0, "negative reference count", &pdoi->cactRef);
|
|
if (pvNil == pdoi->ppdoiPrev)
|
|
Assert(tLinked != tYes, "should be linked");
|
|
else
|
|
{
|
|
Assert(tLinked != tNo, "should NOT be linked");
|
|
|
|
AssertVarMem(pdoi->ppdoiPrev);
|
|
AssertVar(*pdoi->ppdoiPrev == pdoi, "*ppdoiPrev is wrong",
|
|
pdoi->ppdoiPrev);
|
|
if (pdoi->pdoiNext != pvNil)
|
|
{
|
|
AssertVarMem(pdoi->pdoiNext);
|
|
AssertVar(pdoi->pdoiNext->ppdoiPrev == &pdoi->pdoiNext,
|
|
"ppdoiPrev in next is wrong", &pdoi->pdoiNext->ppdoiPrev);
|
|
}
|
|
}
|
|
|
|
_Leave();
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Link object into list.
|
|
***************************************************************************/
|
|
priv void _LinkDoi(DOI *pdoi, DOI **ppdoiFirst)
|
|
{
|
|
_Enter();
|
|
|
|
_AssertDoi(pdoi, tNo);
|
|
AssertVarMem(ppdoiFirst);
|
|
|
|
if (*ppdoiFirst != pvNil)
|
|
{
|
|
_AssertDoi(*ppdoiFirst, tYes);
|
|
(*ppdoiFirst)->ppdoiPrev = &pdoi->pdoiNext;
|
|
}
|
|
pdoi->pdoiNext = *ppdoiFirst;
|
|
pdoi->ppdoiPrev = ppdoiFirst;
|
|
*ppdoiFirst = pdoi;
|
|
_AssertDoi(pdoi, tYes);
|
|
|
|
_Leave();
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Unlink object from list.
|
|
***************************************************************************/
|
|
priv void _UnlinkDoi(DOI *pdoi)
|
|
{
|
|
_Enter();
|
|
|
|
_AssertDoi(pdoi, tYes);
|
|
*pdoi->ppdoiPrev = pdoi->pdoiNext;
|
|
if (pvNil != pdoi->pdoiNext)
|
|
{
|
|
pdoi->pdoiNext->ppdoiPrev = pdoi->ppdoiPrev;
|
|
_AssertDoi(pdoi->pdoiNext, tYes);
|
|
}
|
|
pdoi->ppdoiPrev = pvNil;
|
|
pdoi->pdoiNext = pvNil;
|
|
_AssertDoi(pdoi, tNo);
|
|
|
|
_Leave();
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Called if anyone tries to copy a class with NOCOPY(cls) in its
|
|
declaration.
|
|
***************************************************************************/
|
|
void __AssertOnCopy(void)
|
|
{
|
|
Bug("Copying a non-copyable object");
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Asserts on unmarked allocated (BASE) objects.
|
|
***************************************************************************/
|
|
void AssertUnmarkedObjs(void)
|
|
{
|
|
_Enter();
|
|
|
|
STN stn;
|
|
SZS szs;
|
|
BASE *pbase;
|
|
DOI *pdoi;
|
|
DOI *pdoiLast;
|
|
bool fAssert;
|
|
long cdoiLost = 0;
|
|
long lwThread = LwThreadCur();
|
|
|
|
Assert(_pdoiFirstRaw == pvNil, "Raw list is not empty!");
|
|
|
|
// we want to traverse the list in the reverse order to report problems
|
|
// find the end of the list and see if there are any lost blocks
|
|
pdoiLast = pvNil;
|
|
for (pdoi = _pdoiFirst; pvNil != pdoi; pdoi = pdoi->pdoiNext)
|
|
{
|
|
pdoiLast = pdoi;
|
|
if (pdoi->cactRef == 0 && pdoi->lwThread == lwThread)
|
|
cdoiLost++;
|
|
}
|
|
|
|
if (cdoiLost == 0)
|
|
{
|
|
// no lost blocks
|
|
goto LDone;
|
|
}
|
|
|
|
stn.FFormatSz(PszLit("Total lost objects: %d. Press 'Debugger' for detail"),
|
|
cdoiLost);
|
|
stn.GetSzs(szs);
|
|
fAssert = FAssertProc(__szsFile, __LINE__, szs, pvNil, 0);
|
|
|
|
for (pdoi = pdoiLast; ; )
|
|
{
|
|
pbase = _PbaseFromDoi(pdoi);
|
|
AssertPo(pbase, fobjAllocated);
|
|
|
|
if (pdoi->cactRef == 0 && pdoi->lwThread == lwThread)
|
|
{
|
|
if (fAssert)
|
|
{
|
|
stn.FFormatSz(
|
|
PszLit("\nLost object: cls='%f', size=%d, ")
|
|
PszLit("StackTrace=(use map file)"),
|
|
pbase->Cls(), pdoi->cbTot - size(DOI));
|
|
stn.GetSzs(szs);
|
|
|
|
if (FAssertProc(pdoi->pszsFile, pdoi->lwLine, szs,
|
|
pdoi->rglwStack, kclwStackDoi * size(long)))
|
|
{
|
|
Debugger();
|
|
}
|
|
}
|
|
|
|
MarkMemObj(pbase);
|
|
}
|
|
|
|
if (pdoi->ppdoiPrev == &_pdoiFirst)
|
|
break;
|
|
|
|
// UUUUGGGGH! We don't have a pointer to the previous DOI, we
|
|
// have a pointer to the previous DOI's pdoiNext!
|
|
pdoi = (DOI *)PvSubBv(pdoi->ppdoiPrev, offset(DOI, pdoiNext));
|
|
}
|
|
|
|
LDone:
|
|
_Leave();
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Clears all marks on allocated (BASE) objects.
|
|
***************************************************************************/
|
|
void UnmarkAllObjs(void)
|
|
{
|
|
_Enter();
|
|
|
|
BASE *pbase;
|
|
DOI *pdoi;
|
|
long lwThread = LwThreadCur();
|
|
|
|
Assert(_pdoiFirstRaw == pvNil, "Raw list is not empty!");
|
|
for (pdoi = _pdoiFirst; pvNil != pdoi; pdoi = pdoi->pdoiNext)
|
|
{
|
|
pbase = _PbaseFromDoi(pdoi);
|
|
AssertPo(pbase, fobjAllocated);
|
|
|
|
if (pdoi->lwThread == lwThread)
|
|
pdoi->cactRef = 0;
|
|
}
|
|
|
|
_Leave();
|
|
}
|
|
#endif //DEBUG
|
|
|
|
|
|
/***************************************************************************
|
|
Linked list element constructor
|
|
***************************************************************************/
|
|
BLL::BLL(void)
|
|
{
|
|
_ppbllPrev = pvNil;
|
|
_pbllNext = pvNil;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Remove the element from the linked list
|
|
***************************************************************************/
|
|
BLL::~BLL(void)
|
|
{
|
|
// unlink the thing
|
|
if (_ppbllPrev != pvNil)
|
|
_Attach(pvNil);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Remove the element from the linked list (if it's in one) and reattach
|
|
it at ppbllPrev (if not pvNil).
|
|
***************************************************************************/
|
|
void BLL::_Attach(void *ppbllPrev)
|
|
{
|
|
AssertThis(0);
|
|
PBLL *ppbll = (PBLL *)ppbllPrev;
|
|
AssertNilOrVarMem(ppbll);
|
|
|
|
// unlink the thing
|
|
if (_ppbllPrev != pvNil)
|
|
{
|
|
Assert(*_ppbllPrev == this, "links corrupt");
|
|
if ((*_ppbllPrev = _pbllNext) != pvNil)
|
|
{
|
|
Assert(_pbllNext->_ppbllPrev == &_pbllNext, "links corrupt 2");
|
|
_pbllNext->_ppbllPrev = _ppbllPrev;
|
|
}
|
|
}
|
|
|
|
// link the thing
|
|
if ((_ppbllPrev = ppbll) == pvNil)
|
|
{
|
|
// not in a linked list
|
|
_pbllNext = pvNil;
|
|
return;
|
|
}
|
|
if ((_pbllNext = *ppbll) != pvNil)
|
|
{
|
|
Assert(_pbllNext->_ppbllPrev == ppbll, "links corrupt 3");
|
|
_pbllNext->_ppbllPrev = &_pbllNext;
|
|
}
|
|
*ppbll = this;
|
|
}
|
|
|
|
|
|
#ifdef DEBUG
|
|
/***************************************************************************
|
|
Check the links.
|
|
***************************************************************************/
|
|
void BLL::AssertValid(ulong grf)
|
|
{
|
|
BLL_PAR::AssertValid(grf);
|
|
|
|
if (_pbllNext != pvNil)
|
|
{
|
|
AssertVarMem(_pbllNext);
|
|
Assert(_pbllNext->_ppbllPrev == &_pbllNext, "links corrupt");
|
|
}
|
|
if (_ppbllPrev != pvNil)
|
|
{
|
|
AssertVarMem(_ppbllPrev);
|
|
Assert(*_ppbllPrev == this, "links corrupt 2");
|
|
}
|
|
}
|
|
#endif //DEBUG
|
|
|