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

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