windows-nt/Source/XPSP1/NT/com/ole32/olethunk/olethk32/stalloc.cxx
2020-09-26 16:20:57 +08:00

398 lines
11 KiB
C++

//+---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1992 - 1994.
//
// File: stalloc.cxx
//
// Contents: CStackAllocator
//
// History: 29-Sep-94 DrewB Created
//
// Notes: Loosely based on BobDay's original PSTACK implementation
//
//----------------------------------------------------------------------------
#include "headers.cxx"
#pragma hdrstop
// Pad a count to the given alignment
// Alignment must be 2^n-1
#define ALIGN_CB(cb, align) \
(((cb)+(align)) & ~(align))
//+---------------------------------------------------------------------------
//
// Structure: SStackBlock (sb)
//
// Purpose: Header information for stack blocks
//
// History: 29-Sep-94 DrewB Created
//
//----------------------------------------------------------------------------
struct SStackBlock
{
DWORD dwNextBlock;
DWORD dwStackTop;
};
#define BLOCK_OVERHEAD (sizeof(SStackBlock))
#define BLOCK_START(mem) ((mem)+BLOCK_OVERHEAD)
#define BLOCK_AVAILABLE(cb) ((cb)-BLOCK_OVERHEAD)
//+---------------------------------------------------------------------------
//
// Function: CStackAllocator::CStackAllocator, public
//
// Arguments: [pmm] - Memory model to use
// [cbBlock] - Size of chunk to allocate when necessary
// [cbAlignment] - Alignment size, must be 2^N
//
// History: 29-Sep-94 DrewB Created
//
//----------------------------------------------------------------------------
CStackAllocator::CStackAllocator(CMemoryModel *pmm,
DWORD cbBlock,
DWORD cbAlignment)
{
thkAssert(BLOCK_AVAILABLE(cbBlock) > 0);
// Ensure that the alignment is a power of two
thkAssert((cbAlignment & (cbAlignment-1)) == 0);
// Store alignment - 1 since that's the actual value we need for
// alignment computations
_cbAlignment = cbAlignment-1;
// Ensure that overhead and tracking will not affect alignment
thkAssert(ALIGN_CB(BLOCK_OVERHEAD, _cbAlignment) == BLOCK_OVERHEAD &&
ALIGN_CB(sizeof(SStackMemTrace), _cbAlignment) ==
sizeof(SStackMemTrace));
_pmm = pmm;
_cbBlock = cbBlock;
_dwBlocks = 0;
_dwCurrent = 0;
_cbAvailable = 0;
_psaNext = NULL;
_fActive = TRUE;
}
//+---------------------------------------------------------------------------
//
// Member: CStackAllocator::~CStackAllocator, public virtual
//
// History: 29-Sep-94 DrewB Created
//
//----------------------------------------------------------------------------
CStackAllocator::~CStackAllocator(void)
{
Reset();
}
//+---------------------------------------------------------------------------
//
// Function: CStackAllocator::Alloc, public
//
// Synopsis: Allocates a chunk of memory from the stack
//
// Arguments: [cb] - Amount of memory to allocate
//
// Returns: Pointer to memory or NULL
//
// History: 29-Sep-94 DrewB Created
//
//----------------------------------------------------------------------------
DWORD CStackAllocator::Alloc(DWORD cb)
{
DWORD dwMem;
thkAssert(cb > 0);
// Round size up to maintain alignment of stack
cb = ALIGN_CB(cb, _cbAlignment);
#if DBG == 1
// Reserve space to record caller
cb += sizeof(SStackMemTrace);
#endif
thkAssert(cb <= BLOCK_AVAILABLE(_cbBlock));
// Check to see if the current block can hold the new allocation
if (cb > _cbAvailable)
{
DWORD dwBlock;
SStackBlock UNALIGNED *psb;
// It's too big, so allocate a new block
dwBlock = _pmm->AllocMemory(_cbBlock);
if (dwBlock == 0)
{
return 0;
}
if (_dwBlocks != 0)
{
// Update current top block
psb = (SStackBlock UNALIGNED *)
_pmm->ResolvePtr(_dwBlocks, sizeof(SStackBlock));
psb->dwStackTop = _dwCurrent;
_pmm->ReleasePtr(_dwBlocks);
}
// Make the new block the top block
psb = (SStackBlock UNALIGNED *)
_pmm->ResolvePtr(dwBlock, sizeof(SStackBlock));
psb->dwNextBlock = _dwBlocks;
_dwBlocks = dwBlock;
_pmm->ReleasePtr(dwBlock);
_dwCurrent = BLOCK_START(dwBlock);
_cbAvailable = BLOCK_AVAILABLE(_cbBlock);
}
thkAssert(_cbAvailable >= cb);
dwMem = _dwCurrent;
_dwCurrent += cb;
_cbAvailable -= cb;
#if DBG == 1
void *pvMem;
// Fill memory to show reuse problems
pvMem = _pmm->ResolvePtr(dwMem, cb);
memset(pvMem, 0xED, cb);
_pmm->ReleasePtr(dwMem);
#endif
#if DBG == 1
SStackMemTrace UNALIGNED *psmt;
psmt = (SStackMemTrace UNALIGNED *)
_pmm->ResolvePtr(_dwCurrent-sizeof(SStackMemTrace),
sizeof(SStackMemTrace));
psmt->cbSize = cb-sizeof(SStackMemTrace);
#if !defined(_CHICAGO_)
//
// On RISC platforms, psmt points to an unaligned structure.
// Use a temp variable so we don't get an alignment fault
// when RtlGetCallersAddress returns the value.
//
void *pv;
void *pvCaller;
RtlGetCallersAddress(&pvCaller, &pv);
psmt->pvCaller = pvCaller;
#else
// Depends on return address being directly below first argument
psmt->pvCaller = *((void **)&cb-1);
#endif
thkDebugOut((DEB_MEMORY, "Stack: %p alloc 0x%08lX:%3d, avail %d\n",
psmt->pvCaller, dwMem, cb, _cbAvailable));
_pmm->ReleasePtr(_dwCurrent-sizeof(SStackMemTrace));
#endif
return dwMem;
}
//+---------------------------------------------------------------------------
//
// Function: CStackAllocator::Free, public
//
// Synopsis: Frees allocated memory
//
// Arguments: [dwMem] - Memory
// [cb] - Amount of memory allocated
//
// History: 29-Sep-94 DrewB Created
//
//----------------------------------------------------------------------------
void CStackAllocator::Free(DWORD dwMem, DWORD cb)
{
thkAssert(dwMem != 0);
thkAssert(cb > 0);
// Round size up to maintain alignment of stack
cb = ALIGN_CB(cb, _cbAlignment);
#if DBG == 1
cb += sizeof(SStackMemTrace);
#endif
thkAssert(cb <= BLOCK_AVAILABLE(_cbBlock));
#if DBG == 1
void *pvCaller;
#if !defined(_CHICAGO_)
void *pv;
RtlGetCallersAddress(&pvCaller, &pv);
#else
// Depends on return address being directly below first argument
pvCaller = *((void **)&dwMem-1);
#endif
thkDebugOut((DEB_MEMORY, "Stack: %p frees 0x%08lX:%3d, avail %d\n",
pvCaller, dwMem, cb, _cbAvailable));
#endif
#if DBG == 1
if (_dwCurrent-cb != dwMem)
{
thkDebugOut((DEB_ERROR, "Free of %d:%d is not TOS (0x%08lX)\n",
dwMem, cb, _dwCurrent));
thkAssert(_dwCurrent-cb == dwMem);
}
#endif
_dwCurrent -= cb;
_cbAvailable += cb;
#if DBG == 1
void *pvMem;
// Fill memory to show reuse problems
pvMem = _pmm->ResolvePtr(dwMem, cb);
memset(pvMem, 0xDD, cb);
_pmm->ReleasePtr(dwMem);
#endif
if (_dwCurrent == BLOCK_START(_dwBlocks))
{
SStackBlock UNALIGNED *psb;
DWORD dwBlock;
// If we've just freed up an entire block and it's not the
// only block for the stack, free the block itself and
// restore stack state from the next block
// We keep the first block around forever to avoid memory
// thrashing
psb = (SStackBlock UNALIGNED *)
_pmm->ResolvePtr(_dwBlocks, sizeof(SStackBlock));
dwBlock = psb->dwNextBlock;
_pmm->ReleasePtr(_dwBlocks);
if (dwBlock != 0)
{
_pmm->FreeMemory(_dwBlocks);
_dwBlocks = dwBlock;
psb = (SStackBlock UNALIGNED *)
_pmm->ResolvePtr(_dwBlocks, sizeof(SStackBlock));
_dwCurrent = psb->dwStackTop;
_cbAvailable = _cbBlock-(_dwCurrent-_dwBlocks);
_pmm->ReleasePtr(_dwBlocks);
}
}
}
//+---------------------------------------------------------------------------
//
// Member: CStackAllocator::Reset, public
//
// Synopsis: Releases all memory in the stack
//
// History: 29-Sep-94 DrewB Created
//
//----------------------------------------------------------------------------
void CStackAllocator::Reset(void)
{
DWORD dwBlock;
SStackBlock UNALIGNED *psb;
while (_dwBlocks != 0)
{
psb = (SStackBlock UNALIGNED *)
_pmm->ResolvePtr(_dwBlocks, sizeof(SStackBlock));
dwBlock = psb->dwNextBlock;
_pmm->ReleasePtr(_dwBlocks);
_pmm->FreeMemory(_dwBlocks);
_dwBlocks = dwBlock;
}
_dwCurrent = 0;
_cbAvailable = 0;
}
//+---------------------------------------------------------------------------
//
// Function: CStackAllocator::RecordState, public debug
//
// Synopsis: Records the current state of the stack
//
// Arguments: [psr] - Storage space for information
//
// Modifies: [psr]
//
// History: 28-Apr-94 DrewB Created
//
//----------------------------------------------------------------------------
#if DBG == 1
void CStackAllocator::RecordState(SStackRecord *psr)
{
psr->dwStackPointer = _dwCurrent;
psr->dwThreadId = GetCurrentThreadId();
}
#endif
//+---------------------------------------------------------------------------
//
// Function: CStackAllocator::CheckState, public debug
//
// Synopsis: Checks recorded information about the stack against its
// current state
//
// Arguments: [psr] - Recorded information
//
// History: 28-Apr-94 DrewB Created
//
//----------------------------------------------------------------------------
#if DBG == 1
void CStackAllocator::CheckState(SStackRecord *psr)
{
thkAssert(psr->dwThreadId == GetCurrentThreadId());
if ((psr->dwStackPointer != 0 && psr->dwStackPointer != _dwCurrent) ||
(psr->dwStackPointer == 0 &&
_dwCurrent != 0 && _dwCurrent != BLOCK_START(_dwBlocks)))
{
thkDebugOut((DEB_ERROR, "Stack alloc change: 0x%08lX to 0x%08lX\n",
psr->dwStackPointer, _dwCurrent));
if (_dwCurrent > BLOCK_START(_dwBlocks))
{
SStackMemTrace UNALIGNED *psmt;
psmt = (SStackMemTrace UNALIGNED *)
_pmm->ResolvePtr(_dwCurrent-sizeof(SStackMemTrace),
sizeof(SStackMemTrace));
thkDebugOut((DEB_ERROR, "Top alloc: %d bytes by %p\n",
psmt->cbSize, psmt->pvCaller));
_pmm->ReleasePtr(_dwCurrent-sizeof(SStackMemTrace));
}
thkAssert(!((psr->dwStackPointer != 0 &&
psr->dwStackPointer != _dwCurrent) ||
(psr->dwStackPointer == 0 &&
_dwCurrent != 0 ||
_dwCurrent != BLOCK_START(_dwBlocks))));
}
}
#endif