398 lines
11 KiB
C++
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
|