594 lines
14 KiB
C++
594 lines
14 KiB
C++
/* Copyright (c) Microsoft Corporation.
|
|
Licensed under the MIT License. */
|
|
|
|
/***************************************************************************
|
|
Author: ShonK
|
|
Project: Kauai
|
|
Reviewed:
|
|
Copyright (c) Microsoft Corporation
|
|
|
|
Shared (between Mac and Win) memory allocation routines.
|
|
|
|
The APIs in this module implement fixed block (non-moveable,
|
|
non-resizeable) memory management. On win, we use GlobalAlloc; on Mac
|
|
we use ::operator new. The win hq is based on FAllocPv. The mac
|
|
hq is a Mac handle. _FResizePpv is win only (needed for
|
|
resizing HQs) and considered private to the memory management code
|
|
(if the implementation of Win HQs changes, it will go away).
|
|
***************************************************************************/
|
|
#include "util.h"
|
|
ASSERTNAME
|
|
|
|
|
|
#ifdef DEBUG
|
|
const long kclwStackMbh = 5;
|
|
|
|
//memory block header
|
|
struct MBH
|
|
{
|
|
long cb; // size of block, including header and footer
|
|
PSZS pszsFile; // source file that allocation request is coming from
|
|
long lwLine; // line in file that allocation request is coming from
|
|
long lwThread; // thread id
|
|
MBH *pmbhPrev; // previous allocated block (in doubly linked list)
|
|
MBH *pmbhNext; // next allocated block
|
|
long rglwStack[kclwStackMbh]; // the EBP/A6 chain
|
|
short cactRef; // for marking memory
|
|
short swMagic; // magic number, to detect memory trashing
|
|
};
|
|
|
|
//memory block footer
|
|
struct MBF
|
|
{
|
|
short swMagic; // magic number, to detect memory trashing
|
|
};
|
|
|
|
MBH *_pmbhFirst; // head of the doubly linked list
|
|
|
|
|
|
priv void _LinkMbh(MBH *pmbh);
|
|
priv void _UnlinkMbh(MBH *pmbh, MBH *pmbhOld);
|
|
priv void _AssertMbh(MBH *pmbh);
|
|
#endif //DEBUG
|
|
|
|
|
|
#ifdef MAC
|
|
#define malloc(cb) ::operator new(cb)
|
|
#define free(pv) delete (pv)
|
|
#endif //MAC
|
|
#ifdef WIN
|
|
#define malloc(cb) (void *)GlobalAlloc(GMEM_FIXED, cb)
|
|
#define free(pv) GlobalFree((HGLOBAL)pv)
|
|
#define _msize(pv) GlobalSize((HGLOBAL)pv)
|
|
#define realloc(pv, cb) (void *)GlobalReAlloc((HGLOBAL)pv, cb, GMEM_MOVEABLE)
|
|
#endif //WIN
|
|
|
|
|
|
PFNLIB vpfnlib = pvNil;
|
|
bool _fInLiberator = fFalse;
|
|
|
|
|
|
#ifdef DEBUG
|
|
/***************************************************************************
|
|
Do simulated failure testing.
|
|
***************************************************************************/
|
|
bool DMAGL::FFail(void)
|
|
{
|
|
bool fRet = fFalse;
|
|
|
|
vmutxMem.Enter();
|
|
if (cactFail > 0)
|
|
{
|
|
if (cactDo <= 0)
|
|
{
|
|
cactFail--;
|
|
fRet = fTrue;
|
|
}
|
|
else
|
|
cactDo--;
|
|
}
|
|
vmutxMem.Leave();
|
|
|
|
return fRet;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Update values after an allocation
|
|
***************************************************************************/
|
|
void DMAGL::Allocate(long cbT)
|
|
{
|
|
vmutxMem.Enter();
|
|
if (cvRun < ++cv)
|
|
cvRun = cv;
|
|
cvTot++;
|
|
if (cbRun < (cb += cbT))
|
|
cbRun = cb;
|
|
vmutxMem.Leave();
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Update values after a resize
|
|
***************************************************************************/
|
|
void DMAGL::Resize(long dcb)
|
|
{
|
|
vmutxMem.Enter();
|
|
if (cbRun < (cb += dcb))
|
|
cbRun = cb;
|
|
vmutxMem.Leave();
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Update values after a block is freed
|
|
***************************************************************************/
|
|
void DMAGL::Free(long cbT)
|
|
{
|
|
--cv;
|
|
cb -= cbT;
|
|
}
|
|
#endif //DEBUG
|
|
|
|
|
|
/***************************************************************************
|
|
Allocates a fixed block.
|
|
***************************************************************************/
|
|
#ifdef DEBUG
|
|
bool FAllocPvDebug(void **ppv, long cb, ulong grfmem, long mpr,
|
|
PSZS pszsFile, long lwLine, DMAGL *pdmagl)
|
|
#else //!DEBUG
|
|
bool FAllocPv(void **ppv, long cb, ulong grfmem, long mpr)
|
|
#endif //!DEBUG
|
|
{
|
|
AssertVarMem(ppv);
|
|
AssertIn(cb, 0, kcbMax);
|
|
long cbFree;
|
|
|
|
if (cb > kcbMax)
|
|
{
|
|
BugVar("who's allocating a humongous block?", &cb);
|
|
goto LFail;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
// do simulated failure
|
|
if (pdmagl->FFail())
|
|
goto LFail;
|
|
|
|
vmutxMem.Enter();
|
|
if (pvNil != _pmbhFirst)
|
|
_AssertMbh(_pmbhFirst);
|
|
vmutxMem.Leave();
|
|
|
|
Assert(cb + size(MBH) + size(MBF) > cb, 0);
|
|
cb += size(MBH) + size(MBF);
|
|
#endif //DEBUG
|
|
|
|
for (;;)
|
|
{
|
|
*ppv = malloc(cb);
|
|
if (pvNil != *ppv || pvNil == vpfnlib)
|
|
break;
|
|
|
|
vmutxMem.Enter();
|
|
if (_fInLiberator)
|
|
cbFree = 0;
|
|
else
|
|
{
|
|
_fInLiberator = fTrue;
|
|
vmutxMem.Leave();
|
|
cbFree = (*vpfnlib)(cb, mpr);
|
|
vmutxMem.Enter();
|
|
_fInLiberator = fFalse;
|
|
}
|
|
vmutxMem.Leave();
|
|
|
|
if (cbFree <= 0)
|
|
break;
|
|
}
|
|
|
|
if (pvNil == *ppv)
|
|
{
|
|
LFail:
|
|
*ppv = pvNil;
|
|
PushErc(ercOomPv);
|
|
return fFalse;
|
|
}
|
|
|
|
if (grfmem & fmemClear)
|
|
ClearPb(*ppv, cb);
|
|
#ifdef DEBUG
|
|
else
|
|
FillPb(*ppv, cb, kbGarbage);
|
|
|
|
// fill in the header
|
|
MBH *pmbh = (MBH *)*ppv;
|
|
*ppv = pmbh + 1;
|
|
pmbh->cb = cb;
|
|
pmbh->swMagic = kswMagicMem;
|
|
pmbh->pszsFile = pszsFile;
|
|
pmbh->lwLine = lwLine;
|
|
pmbh->lwThread = LwThreadCur();
|
|
|
|
#ifdef WIN
|
|
// follow the EBP chain....
|
|
long *plw;
|
|
long ilw;
|
|
|
|
__asm { mov plw,ebp }
|
|
for (ilw = 0; ilw < kclwStackMbh; ilw++)
|
|
{
|
|
if (pvNil == plw || IsBadReadPtr(plw, 2 * size(long)) ||
|
|
*plw <= (long)plw)
|
|
{
|
|
pmbh->rglwStack[ilw] = 0;
|
|
plw = pvNil;
|
|
}
|
|
else
|
|
{
|
|
pmbh->rglwStack[ilw] = plw[1];
|
|
plw = (long *)*plw;
|
|
}
|
|
}
|
|
#endif //WIN
|
|
|
|
// write the footer
|
|
MBF mbf;
|
|
mbf.swMagic = kswMagicMem;
|
|
CopyPb(&mbf, PvAddBv(pmbh, cb - size(MBF)), size(MBF));
|
|
|
|
// link the block
|
|
_LinkMbh(pmbh);
|
|
|
|
// update statistics
|
|
pdmagl->Allocate(cb - size(MBF) - size(MBH));
|
|
|
|
AssertPvAlloced(*ppv, cb - size(MBF) - size(MBH));
|
|
#endif //DEBUG
|
|
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
#ifdef WIN
|
|
/***************************************************************************
|
|
Resizes the given block. *ppv may change. If fmemClear, clears any
|
|
newly added space.
|
|
***************************************************************************/
|
|
#ifdef DEBUG
|
|
bool _FResizePpvDebug(void **ppv, long cbNew, long cbOld,
|
|
ulong grfmem, long mpr, DMAGL *pdmagl)
|
|
#else //!DEBUG
|
|
bool _FResizePpv(void **ppv, long cbNew, long cbOld, ulong grfmem, long mpr)
|
|
#endif //!DEBUG
|
|
{
|
|
AssertVarMem(ppv);
|
|
AssertIn(cbNew, 0, kcbMax);
|
|
AssertIn(cbOld, 0, kcbMax);
|
|
AssertPvAlloced(*ppv, cbOld);
|
|
long cbFree;
|
|
void *pvNew, *pvOld;
|
|
|
|
#ifdef DEBUG
|
|
MBH *pmbh = (MBH *)PvSubBv(*ppv, size(MBH));
|
|
_AssertMbh(pmbh);
|
|
#endif //DEBUG
|
|
|
|
if (cbNew > kcbMax)
|
|
{
|
|
BugVar("who's resizing a humongous block?", &cbNew);
|
|
goto LFail;
|
|
}
|
|
|
|
pvOld = *ppv;
|
|
#ifdef DEBUG
|
|
// do simulated failure - we can only fail if the block is growing
|
|
if (cbNew > cbOld && pdmagl->FFail())
|
|
goto LFail;
|
|
|
|
// assert we don't overflow (the limit of kcbMax should ensure this)
|
|
Assert(cbOld + size(MBH) + size(MBF) > cbOld, 0);
|
|
Assert(cbNew + size(MBH) + size(MBF) > cbNew, 0);
|
|
cbOld += size(MBH) + size(MBF);
|
|
cbNew += size(MBH) + size(MBF);
|
|
AssertVar(pmbh->cb == cbOld, "bad cbOld value passed to _FResizePpv", &cbOld);
|
|
|
|
// trash the old stuff
|
|
if (cbOld > cbNew)
|
|
FillPb(PvAddBv(pmbh, cbNew), cbOld - cbNew, kbGarbage);
|
|
pvOld = pmbh;
|
|
#endif //DEBUG
|
|
|
|
for (;;)
|
|
{
|
|
pvNew = realloc(pvOld, cbNew);
|
|
|
|
if (pvNil != pvNew || pvNil == vpfnlib)
|
|
break;
|
|
|
|
vmutxMem.Enter();
|
|
if (_fInLiberator)
|
|
cbFree = 0;
|
|
else
|
|
{
|
|
_fInLiberator = fTrue;
|
|
vmutxMem.Leave();
|
|
cbFree = (*vpfnlib)(cbNew - cbOld, mpr);
|
|
vmutxMem.Enter();
|
|
_fInLiberator = fFalse;
|
|
}
|
|
vmutxMem.Leave();
|
|
|
|
if (cbFree <= 0)
|
|
break;
|
|
}
|
|
|
|
if (pvNil == pvNew)
|
|
{
|
|
Assert(cbOld < cbNew, "why did shrinking fail?");
|
|
LFail:
|
|
AssertPvAlloced(*ppv, cbOld - size(MBH) - size(MBF));
|
|
PushErc(ercOomPv);
|
|
|
|
return fFalse;
|
|
}
|
|
*ppv = pvNew;
|
|
|
|
if ((grfmem & fmemClear) && cbOld < cbNew)
|
|
{
|
|
// Clear the new stuff
|
|
ClearPb(PvAddBv(pvNew, cbOld), cbNew - cbOld);
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
if ((grfmem & fmemClear) && cbOld < cbNew)
|
|
ClearPb(PvAddBv(pvNew, cbOld - size(MBF)), size(MBF));
|
|
else if (cbOld < cbNew)
|
|
{
|
|
// fill the new stuff with garbage
|
|
FillPb(PvAddBv(pvNew, cbOld - size(MBF)), cbNew - cbOld + size(MBF),
|
|
kbGarbage);
|
|
}
|
|
|
|
// update the header
|
|
if (pvNew != pmbh)
|
|
{
|
|
_UnlinkMbh((MBH *)pvNew, pmbh);
|
|
pmbh = (MBH *)pvNew;
|
|
_LinkMbh(pmbh);
|
|
}
|
|
*ppv = pmbh + 1;
|
|
pmbh->cb = cbNew;
|
|
|
|
// write the footer
|
|
MBF mbf;
|
|
mbf.swMagic = kswMagicMem;
|
|
CopyPb(&mbf, PvAddBv(pmbh, cbNew - size(MBF)), size(MBF));
|
|
AssertPvAlloced(*ppv, cbNew - size(MBF) - size(MBH));
|
|
|
|
// update statistics
|
|
pdmagl->Resize(cbNew - cbOld);
|
|
#endif //DEBUG
|
|
|
|
return fTrue;
|
|
}
|
|
#endif //WIN
|
|
|
|
|
|
/***************************************************************************
|
|
If *ppv is not nil, frees it and sets *ppv to nil.
|
|
***************************************************************************/
|
|
#ifdef DEBUG
|
|
void FreePpvDebug(void **ppv, DMAGL *pdmagl)
|
|
#else //!DEBUG
|
|
void FreePpv(void **ppv)
|
|
#endif //!DEBUG
|
|
{
|
|
AssertVarMem(ppv);
|
|
if (*ppv == pvNil)
|
|
return;
|
|
|
|
#ifdef DEBUG
|
|
MBH *pmbh = (MBH *)PvSubBv(*ppv, size(MBH));
|
|
_AssertMbh(pmbh);
|
|
_UnlinkMbh(pmbh, pmbh);
|
|
|
|
// update statistics
|
|
pdmagl->Free(pmbh->cb - size(MBF) - size(MBH));
|
|
|
|
// fill the block with garbage before freeing it
|
|
FillPb(pmbh, pmbh->cb, kbGarbage);
|
|
*ppv = pmbh;
|
|
#endif //DEBUG
|
|
|
|
free(*ppv);
|
|
*ppv = pvNil;
|
|
}
|
|
|
|
|
|
#ifdef DEBUG
|
|
/***************************************************************************
|
|
Link the Mbh into the debug-only doubly linked list.
|
|
***************************************************************************/
|
|
priv void _LinkMbh(MBH *pmbh)
|
|
{
|
|
AssertVarMem(pmbh);
|
|
|
|
vmutxMem.Enter();
|
|
pmbh->pmbhPrev = pvNil;
|
|
pmbh->pmbhNext = _pmbhFirst;
|
|
if (_pmbhFirst != pvNil)
|
|
{
|
|
Assert(_pmbhFirst->pmbhPrev == pvNil, "_pmbhFirst's prev is not nil");
|
|
_pmbhFirst->pmbhPrev = pmbh;
|
|
}
|
|
_pmbhFirst = pmbh;
|
|
vmutxMem.Leave();
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Unlink the MBH from the debug-only doubly linked list. pmbhOld is the
|
|
previous value of the linked block. pmbhOld may not be a valid pointer
|
|
now (when mem is resized).
|
|
***************************************************************************/
|
|
priv void _UnlinkMbh(MBH *pmbh, MBH *pmbhOld)
|
|
{
|
|
AssertVarMem(pmbh);
|
|
Assert(pmbhOld != pvNil, 0);
|
|
|
|
vmutxMem.Enter();
|
|
// update prev's next pointer
|
|
if (pvNil == pmbh->pmbhPrev)
|
|
{
|
|
Assert(_pmbhFirst == pmbhOld, "prev is wrongly nil");
|
|
_pmbhFirst = pmbh->pmbhNext;
|
|
}
|
|
else
|
|
{
|
|
Assert(_pmbhFirst != pmbhOld, "prev should be nil");
|
|
Assert(pmbh->pmbhPrev->pmbhNext == pmbhOld, "prev's next wrong");
|
|
pmbh->pmbhPrev->pmbhNext = pmbh->pmbhNext;
|
|
}
|
|
|
|
// update next's prev pointer
|
|
if (pvNil != pmbh->pmbhNext)
|
|
{
|
|
Assert(pmbh->pmbhNext->pmbhPrev == pmbhOld, "next's prev wrong");
|
|
pmbh->pmbhNext->pmbhPrev = pmbh->pmbhPrev;
|
|
}
|
|
vmutxMem.Leave();
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Validate the MBH and the block that it is the head of.
|
|
***************************************************************************/
|
|
void _AssertMbh(MBH *pmbh)
|
|
{
|
|
short sw;
|
|
|
|
if (vcactSuspendCheckPointers != 0)
|
|
return;
|
|
|
|
vmutxMem.Enter();
|
|
AssertVarMem(pmbh);
|
|
Assert(pmbh->swMagic == kswMagicMem, "bad magic number");
|
|
AssertIn(pmbh->cb - size(MBH) - size(MBF), 0, kcbMax);
|
|
Win( Assert(pmbh->cb <= (long)_msize(pmbh), "bigger than malloced block"); )
|
|
AssertPvCb(pmbh, pmbh->cb);
|
|
if (pmbh->pmbhPrev != pvNil)
|
|
{
|
|
AssertVarMem(pmbh->pmbhPrev);
|
|
Assert(pmbh->pmbhPrev->pmbhNext == pmbh, "wrong next in prev");
|
|
Assert(pmbh != _pmbhFirst, "first has prev!");
|
|
}
|
|
if (pmbh->pmbhNext != pvNil)
|
|
{
|
|
AssertVarMem(pmbh->pmbhNext);
|
|
Assert(pmbh->pmbhNext->pmbhPrev == pmbh, "wrong prev in next");
|
|
}
|
|
|
|
((byte *)&sw)[0] = *(byte *)PvAddBv(pmbh, pmbh->cb - 2);
|
|
((byte *)&sw)[1] = *(byte *)PvAddBv(pmbh, pmbh->cb - 1);
|
|
Assert(sw == kswMagicMem, "bad tail magic number");
|
|
vmutxMem.Leave();
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Assert the validity of an allocated fixed block. Cb is the size.
|
|
If cb is unknown, pass cvNil.
|
|
***************************************************************************/
|
|
void AssertPvAlloced(void *pv, long cb)
|
|
{
|
|
if (vcactSuspendCheckPointers != 0)
|
|
return;
|
|
|
|
Assert(pv != pvNil, "nil pv");
|
|
MBH *pmbh = (MBH *)PvSubBv(pv, size(MBH));
|
|
_AssertMbh(pmbh);
|
|
if (cb != cvNil)
|
|
Assert(pmbh->cb == cb + size(MBH) + size(MBF), "wrong cb");
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Asserts on unmarked blocks.
|
|
***************************************************************************/
|
|
void AssertUnmarkedMem(void)
|
|
{
|
|
MBH *pmbh;
|
|
long lwThread = LwThreadCur();
|
|
|
|
// enter the critical section
|
|
vmutxMem.Enter();
|
|
|
|
for (pmbh = _pmbhFirst; pmbh != pvNil; pmbh = pmbh->pmbhNext)
|
|
{
|
|
_AssertMbh(pmbh);
|
|
if (pmbh->cactRef == 0 && pmbh->lwThread == lwThread)
|
|
{
|
|
STN stn;
|
|
SZS szs;
|
|
|
|
stn.FFormatSz(
|
|
PszLit("\nLost block: size=%d, StackTrace=(use map file)"),
|
|
pmbh->cb);
|
|
stn.GetSzs(szs);
|
|
|
|
if (FAssertProc(pmbh->pszsFile, pmbh->lwLine, szs,
|
|
pmbh->rglwStack, kclwStackMbh * size(long)))
|
|
{
|
|
Debugger();
|
|
}
|
|
}
|
|
}
|
|
Mac( _AssertUnmarkedHqs(); )
|
|
|
|
// leave the critical section
|
|
vmutxMem.Leave();
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Clears all marks on memory blocks.
|
|
***************************************************************************/
|
|
void UnmarkAllMem(void)
|
|
{
|
|
MBH *pmbh;
|
|
long lwThread = LwThreadCur();
|
|
|
|
// enter the critical section
|
|
vmutxMem.Enter();
|
|
|
|
for (pmbh = _pmbhFirst; pmbh != pvNil; pmbh = pmbh->pmbhNext)
|
|
{
|
|
_AssertMbh(pmbh);
|
|
if (pmbh->lwThread == lwThread)
|
|
pmbh->cactRef = 0;
|
|
}
|
|
Mac( _UnmarkAllHqs(); )
|
|
|
|
// leave the critical section
|
|
vmutxMem.Leave();
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Increment the ref count on an allocated pv.
|
|
***************************************************************************/
|
|
void MarkPv(void *pv)
|
|
{
|
|
if (pvNil != pv)
|
|
{
|
|
AssertPvAlloced(pv, cvNil);
|
|
MBH *pmbh = (MBH *)PvSubBv(pv, size(MBH));
|
|
pmbh->cactRef++;
|
|
}
|
|
}
|
|
#endif //DEBUG
|
|
|
|
|