//#--------------------------------------------------------------- // File: CPool.cpp // // Synopsis: This file implements the CPool class // // Copyright (C) 1995 Microsoft Corporation // All rights reserved. // // Authors: HowardCu //---------------------------------------------------------------- #ifdef THIS_FILE #undef THIS_FILE #endif static char __szTraceSourceFile[] = __FILE__; #define THIS_FILE __szTraceSourceFile #include #include "cpool.h" #include "dbgtrace.h" #define PREAMBLE (BYTE)'H' #define POSTAMBLE (BYTE)'C' #define FILLER (BYTE)0xCC // // Define internal Debug structs designed to help find over/underwrites // #ifdef DEBUG #ifndef DISABLE_CPOOL_DEBUG #define CPOOL_DEBUG #endif #endif #ifdef CPOOL_DEBUG #define HEAD_SIGNATURE (DWORD)'daeH' #define TAIL_SIGNATURE (DWORD)'liaT' #define FREE_STATE (DWORD)'eerF' #define USED_STATE (DWORD)'desU' // // forward declaration // class CPoolDebugTail; // // Prefix for CPool instances when in debug mode // class CPoolDebugHead { public: // // declared so normal CPool free list can clobber this member // void* m_pLink; CPoolDebugHead(); ~CPoolDebugHead( void ); void *operator new( size_t cSize, void *pInstance ) { return pInstance; }; void operator delete (void *pInstance) {}; // // Function to mark the instance in use // void MarkInUse( DWORD m_dwSignature, DWORD m_cInstanceSize ); // // Function to mark the instance free // void MarkFree( DWORD m_dwSignature, DWORD m_cInstanceSize ); // // class signature // DWORD m_dwSignature; // // state; either FREE_STATE or USED_STATE // DWORD m_dwState; // // time of allocation // SYSTEMTIME m_time; // // ThreadID which alloc'd/free'd memory // DWORD m_dwThreadID; // // tail pointer used to find the end // CPoolDebugTail UNALIGNED *m_pTailDebug; // // parent CPool signature // DWORD m_dwPoolSignature; // // parent CPool Fragment // LPVOID m_PoolFragment; }; // // Suffix for CPool instances when in debug mode // class CPoolDebugTail { public: CPoolDebugTail(); ~CPoolDebugTail( void ); void *operator new( size_t cSize, void *pInstance ) { return pInstance; }; void operator delete (void *pInstance) {}; // // routine to validate the integrity of the instance // void IsValid( DWORD dwPoolSignature, DWORD cInstanceSize ); // // class signature // DWORD m_dwSignature; // // tail pointer used to find the end // CPoolDebugHead UNALIGNED *m_pHeadDebug; }; //+--------------------------------------------------------------- // // Function: CPoolDebugHead // // Synopsis: constructor; extra init done in def'n // // Arguments: void // // Returns: void // //---------------------------------------------------------------- CPoolDebugHead::CPoolDebugHead( void ) : m_dwState( FREE_STATE ), m_dwSignature( HEAD_SIGNATURE ), m_pTailDebug( NULL ) { // // debug helpers // GetLocalTime( &m_time ) ; m_dwThreadID = GetCurrentThreadId(); } //+--------------------------------------------------------------- // // Function: ~CPoolDebugHead // // Synopsis: destructor; only used to assert error conditions // // Arguments: void // // Returns: void // //---------------------------------------------------------------- CPoolDebugHead::~CPoolDebugHead( void ) { _ASSERT( m_dwSignature == HEAD_SIGNATURE ); } //+--------------------------------------------------------------- // // Function: CPoolDebugHead::MarkInUse // // Synopsis: Called when instance is allocated // // Arguments: DWORD dwPoolSignature: signature of parent pool // DWORD cInstanceSize: instance size of parent pool // // Returns: void // //---------------------------------------------------------------- void CPoolDebugHead::MarkInUse( DWORD dwPoolSignature, DWORD cInstanceSize ) { _ASSERT( m_dwSignature == HEAD_SIGNATURE ); _ASSERT( m_dwState == FREE_STATE ); m_dwState = USED_STATE; // // validate that the application portion is not tampered with // for ( LPBYTE pb = (LPBYTE)(this+1); pb < (LPBYTE)m_pTailDebug; pb++ ) { _ASSERT( *pb == FILLER ); } // // check the validity of the entire instance // m_pTailDebug->IsValid( dwPoolSignature, cInstanceSize ); // // debug helpers // GetLocalTime( &m_time ) ; m_dwThreadID = GetCurrentThreadId(); } //+--------------------------------------------------------------- // // Function: CPoolDebugHead::MarkFree // // Synopsis: Called when instance is freed // // Arguments: DWORD dwPoolSignature: signature of parent pool // DWORD cInstanceSize: instance size of parent pool // // Returns: void // //---------------------------------------------------------------- void CPoolDebugHead::MarkFree( DWORD dwPoolSignature, DWORD cInstanceSize ) { _ASSERT( m_dwSignature == HEAD_SIGNATURE ); // // Check and set the state // _ASSERT( m_dwState == USED_STATE ); m_dwState = FREE_STATE; // // check enough to call IsValid // _ASSERT( m_pTailDebug != 0 ); _ASSERT( (DWORD_PTR)m_pTailDebug > (DWORD_PTR)this ); _ASSERT( m_dwThreadID != 0 ) ; // // check the validity of the entire instance // m_pTailDebug->IsValid( dwPoolSignature, cInstanceSize ); // // set the application data to filler // FillMemory( (LPBYTE)(this+1), (DWORD)((LPBYTE)m_pTailDebug - (LPBYTE)(this+1)), FILLER ); // // debug helpers // GetLocalTime( &m_time ) ; m_dwThreadID = GetCurrentThreadId(); } //+--------------------------------------------------------------- // // Function: CPoolDebugTail // // Synopsis: constructor; extra init done in def'n // // Arguments: void // // Returns: void // //---------------------------------------------------------------- CPoolDebugTail::CPoolDebugTail( void ) : m_dwSignature( TAIL_SIGNATURE ), m_pHeadDebug( NULL ) { } //+--------------------------------------------------------------- // // Function: ~CPoolDebugTail // // Synopsis: destructor; only used to assert error conditions // // Arguments: void // // Returns: void // //---------------------------------------------------------------- CPoolDebugTail::~CPoolDebugTail( void ) { _ASSERT( m_dwSignature == TAIL_SIGNATURE ); } //+--------------------------------------------------------------- // // Function: IsValid // // Synopsis: check validity of instance // // Arguments: DWORD dwPoolSignature: signature of parent pool // DWORD cInstanceSize: instance size of parent pool // // Returns: void // //---------------------------------------------------------------- void CPoolDebugTail::IsValid( DWORD dwPoolSignature, DWORD cInstanceSize ) { _ASSERT( m_dwSignature == TAIL_SIGNATURE ); // // validate that the head is offset at the correct location // _ASSERT( m_pHeadDebug != NULL ); _ASSERT( (UINT_PTR)m_pHeadDebug == (UINT_PTR)(this+1) - cInstanceSize ); // // validate the head structure // _ASSERT( m_pHeadDebug->m_dwSignature == HEAD_SIGNATURE ); _ASSERT( m_pHeadDebug->m_dwPoolSignature == dwPoolSignature ); _ASSERT( m_pHeadDebug->m_pTailDebug == this ); _ASSERT( m_pHeadDebug->m_dwState == FREE_STATE || m_pHeadDebug->m_dwState == USED_STATE ); } #endif //+--------------------------------------------------------------- // // Function: CPool // // Synopsis: constructor // // Arguments: void // // Returns: void // // History: gordm Created 5 Jul 1995 // //---------------------------------------------------------------- CPool::CPool( DWORD dwSignature ) : m_dwSignature( dwSignature ) { TraceFunctEnter( "CPool::CPool" ); m_pFreeList = NULL; m_pExtraFreeLink = NULL; // // Debug variables to help catch heap bugs // m_pLastAlloc = NULL; m_pLastExtraAlloc = NULL; m_cTotalFrees = 0; m_cTotalAllocs = 0; m_cTotalExtraAllocs = 0; m_cInstanceSize = 0; // // Avail + InUse should equal Committed if we're not // in grow/alloc or free. Diagnostic and admin only // This will keep code in critsec as small as possible // m_cNumberAvail = 0; m_cNumberInUse = 0; m_cNumberCommitted = 0; InitializeCriticalSection( &m_PoolCriticalSection ); // // initialize the fragment member variables // m_cFragmentInstances = 0; m_cFragments = 0; ZeroMemory( m_pFragments, sizeof(m_pFragments) ); TraceFunctLeave(); } //+--------------------------------------------------------------- // // Function: ~CPool // // Synopsis: destructor // // Arguments: void // // Returns: void // // History: HowardCu Created 8 May 1995 // //---------------------------------------------------------------- CPool::~CPool( void ) { TraceFunctEnter( "CPool::~CPool" ); _ASSERT( m_cNumberInUse == 0 ); for ( int i=0; ipNext; } m_pLastAlloc = pAlloc; LeaveCriticalSection( &m_PoolCriticalSection ); } else { m_pLastExtraAlloc = pAlloc; m_cTotalExtraAllocs++; } // // alloc failed // if ( pAlloc == NULL ) { InterlockedDecrement( (LPLONG)&m_cNumberInUse ); } else { // // debug/admin use only - ok to do outside of critsec // m_cTotalAllocs++; #ifdef CPOOL_DEBUG CPoolDebugHead* pHead = (CPoolDebugHead*)pAlloc; // // validate that the address in the range // _ASSERT( (char*)pAlloc >= pHead->m_PoolFragment ); _ASSERT( (char*)pAlloc < (char*)pHead->m_PoolFragment + m_cNumberCommitted*m_cInstanceSize ); pHead->MarkInUse( m_dwSignature, m_cInstanceSize ); pAlloc = (Link*)(pHead+1); #endif } #ifdef ALLOC_TRACING DebugTrace( (LPARAM)this, "Alloc: 0x%08X", pAlloc ); TraceFunctLeave(); #endif return (void*)pAlloc; } //+--------------------------------------------------------------- // // Function: Free // // Synopsis: frees the instances // // Arguments: pInstance - a pointer to the CDescriptor // // Returns: void // // History: gordm Created 5 Jul 1995 // //---------------------------------------------------------------- void CPool::Free( void* pInstance ) { #ifdef ALLOC_TRACING TraceFunctEnter( "CPool::Free" ); #endif #ifdef CPOOL_DEBUG CPoolDebugHead* pHead = ((CPoolDebugHead*)pInstance) - 1; // // validate that the address in the range // _ASSERT( (char*)pInstance >=pHead->m_PoolFragment); _ASSERT( (char*)pInstance < (char*)pHead->m_PoolFragment + m_cNumberCommitted*m_cInstanceSize ); pHead->MarkFree( m_dwSignature, m_cInstanceSize ); pInstance = (void*)pHead; #endif IsValid(); _ASSERT(m_cNumberInUse > 0); pInstance = InterlockedExchangePointer( (LPVOID *)&m_pExtraFreeLink, pInstance ); // // free the previous extra pointer if one existed // if ( pInstance != NULL ) { EnterCriticalSection( &m_PoolCriticalSection ); ((Link*)pInstance)->pNext = m_pFreeList; m_pFreeList = (Link*)pInstance; LeaveCriticalSection( &m_PoolCriticalSection ); } // // moved outside of the critsec because it should not be necessary // to protect this variable. We'll think this list is empty only // when we get to this point. This var is inc'd before entering // the critsec and is dec'd if the operation fails. // InterlockedDecrement( (LPLONG)&m_cNumberInUse ); // // debug/admin use only - ok to do outside of critsec - deletes don't fail // m_cTotalFrees++; #ifdef ALLOC_TRACING DebugTrace( (LPARAM)this, "Freed: 0x%08X", pInstance ); TraceFunctLeave(); #endif } // // setup a const DWORD for size manipulation // const DWORD KB = 1024; //+--------------------------------------------------------------- // // Function: ReserveMemory // // Synopsis: Initializes the pool // // Arguments: NumDescriptors - the number of total descriptors in the pool // DescriptorSize - the size of any one descriptor // Signature - object signature // // Returns: TRUE is success, else FALSE // // History: HowardCu Created 8 May 1995 // //---------------------------------------------------------------- BOOL CPool::ReserveMemory( DWORD MaxInstances, DWORD InstanceSize, DWORD IncrementSize ) { TraceFunctEnter( "CPool::ReserveMemory" ); DWORD cFragments; DWORD cFragmentInstances; DWORD cIncrementInstances; _ASSERT( MaxInstances != 0 ); _ASSERT( InstanceSize >= sizeof(struct Link) ); #ifdef CPOOL_DEBUG InstanceSize += sizeof( CPoolDebugHead ) + sizeof( CPoolDebugTail ); #endif if ( IncrementSize == DEFAULT_ALLOC_INCREMENT ) { // // ensure we go to the OS for at least 8 instances at a time // if ( InstanceSize <= 4*KB / 8 ) { cIncrementInstances = 4*KB / InstanceSize; } else if ( InstanceSize <= 64*KB / 8 ) { cIncrementInstances = 64*KB / InstanceSize; } else { cIncrementInstances = min( MaxInstances, 8 ); } } else { cIncrementInstances = IncrementSize; } // // now calculate the number larger fragments // if ( cIncrementInstances > MaxInstances ) { // // no need for CPool; but we shouldn't alloc more than necessary // cFragmentInstances = cIncrementInstances = MaxInstances; cFragments = 1; } else { // // Round up MaxInstances to a integral number of IncrementSize // MaxInstances += cIncrementInstances - 1; MaxInstances /= cIncrementInstances; MaxInstances *= cIncrementInstances; // // as an initial attempt divide the number of instances by max frags // cFragmentInstances = (MaxInstances + MAX_CPOOL_FRAGMENTS - 1) / MAX_CPOOL_FRAGMENTS; if ( cFragmentInstances == 0 ) { cFragmentInstances = MaxInstances; cFragments = 1; } else { // // round up the number of instances in a fragment to an // integral number of IncrementSizes // cFragmentInstances += cIncrementInstances - 1; cFragmentInstances /= cIncrementInstances; cFragmentInstances *= cIncrementInstances; // // recalculate the number of fragments required based on the integral // number of IncrementSizes ( last one may no longer be required ) // cFragments = (MaxInstances + cFragmentInstances - 1) / cFragmentInstances; } } _ASSERT( cFragments > 0 ); _ASSERT( cFragments*cFragmentInstances >= MaxInstances ); m_cInstanceSize = InstanceSize; m_cMaxInstances = MaxInstances; m_cFragments = cFragments; m_cFragmentInstances = cFragmentInstances; m_cIncrementInstances = cIncrementInstances; TraceFunctLeave(); return TRUE; } //+--------------------------------------------------------------- // // Function: ReleaseMemory // // Synopsis: Releases the pool // // Arguments: none // // Returns: TRUE is success, else FALSE // // History: HowardCu Created 8 May 1995 // //---------------------------------------------------------------- BOOL CPool::ReleaseMemory( void ) { TraceFunctEnter( "CPool::ReleaseMemory" ); BOOL bFree = TRUE; DWORD i, cStart; EnterCriticalSection( &m_PoolCriticalSection ); for ( i=cStart=0; im_pTailDebug = pTail; pTail->m_pHeadDebug = pHead; // // helps with debugging to see the parent CPool signature // pHead->m_dwPoolSignature = dwPoolSignature; // // helps with asserts for valid ranges // pHead->m_PoolFragment = pPoolFragment; // // fake out the state before calling mark Free // pHead->m_dwState = USED_STATE; pHead->MarkFree( dwPoolSignature, cInstanceSize ); } #endif //+--------------------------------------------------------------- // // Function: GrowPool // // Synopsis: grows the number of committed instances in the pool // // Arguments: void // // Returns: void // // History: gordm Created 5 Jul 1995 // //---------------------------------------------------------------- void CPool::GrowPool( void ) { #ifdef ALLOC_TRACING TraceFunctEnter( "CPool::GrowPool" ); #endif DWORD cFragment = m_cNumberCommitted / m_cFragmentInstances; DWORD cStart = m_cNumberCommitted % m_cFragmentInstances; DWORD cbSize = (cStart+m_cIncrementInstances) * m_cInstanceSize; #ifdef ALLOC_TRACING DebugTrace( (LPARAM)this, "Expanding the pool to %d descriptors", cNewCommit ); #endif // // if we're starting a new fragment // if ( cStart == 0 ) { // // if we are at a boundary of a fragment Reserve the next fragment m_pFragments[cFragment] = VirtualAlloc( NULL, m_cFragmentInstances*m_cInstanceSize, MEM_RESERVE | MEM_TOP_DOWN, PAGE_NOACCESS ); if ( m_pFragments[cFragment] == NULL ) { #ifdef ALLOC_TRACING ErrorTrace( (LPARAM)this, "Could not reserve more memory: error = %d", GetLastError() ); #endif return; } } LPVOID pHeap = m_pFragments[cFragment]; if ( VirtualAlloc( pHeap, cbSize, MEM_COMMIT, PAGE_READWRITE ) != NULL ) { char* pStart = (char*)pHeap + cStart*m_cInstanceSize; char* pLast = (char*)pHeap + cbSize - m_cInstanceSize; // // run the list joining the next pointers // possible because we own the critsec // for ( char* p=pStart; ppNext = (Link*)(p+m_cInstanceSize); } // // terminate and then set the head to beginning of new list // #ifdef CPOOL_DEBUG InitDebugInstance( pLast, m_dwSignature, m_cInstanceSize, pHeap ); #endif ((Link*)pLast)->pNext = NULL; m_pFreeList = (Link*)pStart; m_cNumberCommitted += m_cIncrementInstances; } #ifdef ALLOC_TRACING else { ErrorTrace( (LPARAM)this, "Could not commit another descriptor: error = %d", GetLastError() ); } TraceFunctLeave(); #endif } //+--------------------------------------------------------------- // // Function: GetContentionCount // // Synopsis: Returns the contention count on the alloc/free // critsec // // Arguments: void // // Returns: the actual count // //---------------------------------------------------------------- DWORD CPool::GetContentionCount( void ) { return m_PoolCriticalSection.DebugInfo != NULL ? m_PoolCriticalSection.DebugInfo->ContentionCount : 0 ; } //+--------------------------------------------------------------- // // Function: GetEntryCount // // Synopsis: Returns the entry count on the alloc/free // critsec // // Arguments: void // // Returns: the actual count // //---------------------------------------------------------------- DWORD CPool::GetEntryCount( void ) { return m_PoolCriticalSection.DebugInfo != NULL ? m_PoolCriticalSection.DebugInfo->EntryCount : 0 ; } //+--------------------------------------------------------------- // // Function: GetInstanceSize // // Synopsis: Returns the application's instance size // // Arguments: void // // Returns: the instance size of the app // //---------------------------------------------------------------- DWORD CPool::GetInstanceSize( void ) { #ifdef CPOOL_DEBUG return m_cInstanceSize - sizeof(CPoolDebugHead) - sizeof(CPoolDebugTail); #else return m_cInstanceSize; #endif } #ifdef DEBUG //+--------------------------------------------------------------- // // Function: IsValid // // Synopsis: Validates the pool signature // // Arguments: void // // Returns: TRUE is success, else FALSE // // History: HowardCu Created 8 May 1995 // //---------------------------------------------------------------- inline void CPool::IsValid( void ) { _ASSERT( m_cMaxInstances != 0 ); _ASSERT( m_cInstanceSize >= sizeof(struct Link) ); _ASSERT( m_cIncrementInstances != 0 ); _ASSERT( m_dwSignature != 0 ); } #endif