|
|
//+---------------------------------------------------------------------------
//
// 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
|