/*========================================================================== * * Copyright (C) 1995 - 2000 Microsoft Corporation. All Rights Reserved. * * File: LockedPool.h * Content: Pool manager for classes * * History: * Date By Reason * ====== == ====== * 12-18-97 aarono Original * 11-06-98 ejs Add custom handler for Release function * 04-12-99 johnkan Trimmed unused functions and parameters, added size assert * 01-31-2000 johnkan Added code to check for items already being in the pool on Release(). * 02-08-2000 johnkan Derived from ClassFPM.h * 03-26-2000 johnkan Renamed to avoid collisions with other classes * 04-06-2000 johnkan Modified to have a base class to derive pool items from * 03-20-2001 vanceo Added thread-local versions ***************************************************************************/ #ifndef __LOCKED_POOL__ #define __LOCKED_POOL__ #undef DPF_SUBCOMP #define DPF_SUBCOMP DN_SUBCOMP_COMMON #define TRACK_POOL_STATS //********************************************************************** // Constant definitions //********************************************************************** #define CHECK_FOR_DUPLICATE_LOCKED_POOL_RELEASE //********************************************************************** // Macro definitions //********************************************************************** #ifndef OFFSETOF // Macro to compute the offset of an element inside of a larger structure (copied from MSDEV's STDLIB.H) #define OFFSETOF(s,m) ( (INT_PTR) &(((s *)0)->m) ) #define __LOCAL_OFFSETOF_DEFINED__ #endif // OFFSETOF //********************************************************************** // Structure definitions //********************************************************************** // // forward reference // class CLockedPoolItem; template< class T > class CLockedPool; //********************************************************************** // Variable definitions //********************************************************************** //********************************************************************** // Function prototypes //********************************************************************** //********************************************************************** // Class definitions //********************************************************************** // // class to act as a link in the pool // class CLockedPoolItem { public: #undef DPF_MODNAME #define DPF_MODNAME "CLockedPoolItem::CLockedPoolItem" CLockedPoolItem() { m_iRefCount = 0; m_pNext = NULL; } #undef DPF_MODNAME #define DPF_MODNAME "CLockedPoolItem::~CLockedPoolItem" virtual ~CLockedPoolItem() { DNASSERT( m_iRefCount == 0 ); } #undef DPF_MODNAME #define DPF_MODNAME "CLockedPoolItem::AddRef" void AddRef( void ) { DNASSERT( m_iRefCount != -1 ); InterlockedIncrement( const_cast( &m_iRefCount ) ); } #undef DPF_MODNAME #define DPF_MODNAME "CLockedPoolItem::DecRef" void DecRef( void ) { DNASSERT( m_iRefCount != 0 ); if ( InterlockedDecrement( const_cast( &m_iRefCount ) ) == 0 ) { ReturnSelfToPool(); } } CLockedPoolItem *GetNext( void ) const { return m_pNext; } void InvalidateNext( void ) { m_pNext = NULL; } #undef DPF_MODNAME #define DPF_MODNAME "CLockedPoolItem::LinkToPool" void LinkToPool( CLockedPoolItem *volatile *const ppPoolItems ) { DNASSERT( ppPoolItems != NULL ); m_pNext = *ppPoolItems; *ppPoolItems = this; } // // Default initialization and deinitialization functions. These can // be overridden by the derived classes. // virtual BOOL PoolAllocFunction( void ){ return TRUE; } virtual BOOL PoolInitFunction( void ){ return TRUE; } virtual void PoolReleaseFunction( void ){}; virtual void PoolDeallocFunction( void ){}; protected: private: // // reference count used to return this item to the pool // volatile LONG m_iRefCount; // // pointer used to link this item to the rest of the pool // CLockedPoolItem *m_pNext; virtual void ReturnSelfToPool( void ) = 0; // // prevent unwarranted copies // CLockedPoolItem( const CLockedPoolItem & ); CLockedPoolItem& operator=( const CLockedPoolItem & ); }; // // class to manage the pool // template< class T > class CLockedPool { public: CLockedPool(); ~CLockedPool(); void Lock( void ) { DNEnterCriticalSection( &m_Lock ); } void Unlock( void ) { DNLeaveCriticalSection( &m_Lock ); } BOOL Initialize( void ); void Deinitialize( void ); T *Get( void ); void Release( T *const pItem ); protected: private: DNCRITICAL_SECTION m_Lock; CLockedPoolItem *volatile m_pPool; // pointer to list of available elements DEBUG_ONLY( BOOL m_fInitialized ); DEBUG_ONLY( volatile LONG m_lOutstandingItemCount ); T *RemoveNode( void ) { T *pReturn; if ( m_pPool != NULL ) { pReturn = static_cast( m_pPool ); m_pPool = m_pPool->GetNext(); DEBUG_ONLY( pReturn->InvalidateNext() ); } else { pReturn = NULL; } return pReturn; } // // prevent unwarranted copies // CLockedPool< T >( const CLockedPool< T > & ); CLockedPool< T >& operator=( const CLockedPool< T > & ); }; //********************************************************************** // Class function definitions //********************************************************************** //********************************************************************** // ------------------------------ // CLockedPool::CLockedPool - constructor // // Entry: Nothing // // Exit: Nothing // ------------------------------ #undef DPF_MODNAME #define DPF_MODNAME "CLockedPool::CLockedPool" template< class T > CLockedPool< T >::CLockedPool(): m_pPool( NULL ) { DEBUG_ONLY( m_fInitialized = FALSE ); DEBUG_ONLY( m_lOutstandingItemCount = 0 ); } //********************************************************************** //********************************************************************** // ------------------------------ // CLockedPool::~CLockedPool - destructor // // Entry: Nothing // // Exit: Nothing // ------------------------------ #undef DPF_MODNAME #define DPF_MODNAME "CLockedPool::~CLockedPool" template< class T > CLockedPool< T >::~CLockedPool() { DEBUG_ONLY( DNASSERT( m_lOutstandingItemCount == 0 ) ); DEBUG_ONLY( DNASSERT( m_fInitialized == FALSE ) ); } //********************************************************************** //********************************************************************** // ------------------------------ // CLockedPool::Initialize - initialize pool // // Entry: Nothing // // Exit: Boolean indicating success // TRUE = initialization succeeded // FALSE = initialization failed // ------------------------------ #undef DPF_MODNAME #define DPF_MODNAME "CLockedPool::Initialize" template< class T > BOOL CLockedPool< T >::Initialize( void ) { BOOL fReturn; DEBUG_ONLY( DNASSERT( m_fInitialized == FALSE ) ); fReturn = TRUE; if ( DNInitializeCriticalSection( &m_Lock ) == FALSE ) { fReturn = FALSE; goto Exit; } DebugSetCriticalSectionRecursionCount( &m_Lock, 0 ); DEBUG_ONLY( m_fInitialized = TRUE ); Exit: return fReturn; } //********************************************************************** //********************************************************************** // ------------------------------ // CLockedPool::Deinitialize - deinitialize pool // // Entry: Nothing // // Exit: Nothing // ------------------------------ #undef DPF_MODNAME #define DPF_MODNAME "CLockedPool::Deinitialize" template< class T > void CLockedPool< T >::Deinitialize( void ) { DEBUG_ONLY( DNASSERT( m_fInitialized != FALSE ) ); Lock(); DEBUG_ONLY( DNASSERT( m_lOutstandingItemCount == 0 ) ); while ( m_pPool != NULL ) { CLockedPoolItem *pNode; pNode = RemoveNode(); pNode->PoolDeallocFunction(); delete pNode; } Unlock(); DNDeleteCriticalSection( &m_Lock ); DEBUG_ONLY( m_fInitialized = FALSE ); } //********************************************************************** //********************************************************************** // ------------------------------ // CLockedPool::Get - get an item from the pool // // Entry: Nothing // // Exit: Pointer to item // NULL = out of memory // ------------------------------ #undef DPF_MODNAME #define DPF_MODNAME "CLockedPool::Get" template< class T > T *CLockedPool< T >::Get( void ) { T *pReturn; DEBUG_ONLY( DNASSERT( m_fInitialized != FALSE ) ); // // initialize // pReturn = NULL; Lock(); // // if the pool is empty, try to allocate a new entry, otherwise grab // the first item from the pool // if ( m_pPool == NULL ) { Unlock(); pReturn = new T; if ( pReturn != NULL ) { if ( pReturn->PoolAllocFunction() == FALSE ) { delete pReturn; pReturn = NULL; } } } else { pReturn = RemoveNode(); Unlock(); } // // if we have an entry (it was freshly created, or removed from the pool), // attempt to initialize it before passing it to the user // if ( pReturn != NULL ) { if ( pReturn->PoolInitFunction() == FALSE ) { Lock(); pReturn->LinkToPool( &m_pPool ); Unlock(); pReturn = NULL; } else { pReturn->SetOwningPool( this ); pReturn->AddRef(); DEBUG_ONLY( InterlockedIncrement( const_cast( &m_lOutstandingItemCount ) ) ); } } return pReturn; } //********************************************************************** //********************************************************************** // ------------------------------ // CLockedPool::Release - return item to pool // // Entry: Pointer to item // // Exit: Nothing // ------------------------------ #undef DPF_MODNAME #define DPF_MODNAME "CLockedPool::Release" template< class T > void CLockedPool< T >::Release( T *const pItem ) { DEBUG_ONLY( DNASSERT( m_fInitialized != FALSE ) ); DNASSERT( pItem != NULL ); DEBUG_ONLY( DNASSERT( pItem->GetNext() == NULL ) ); pItem->PoolReleaseFunction(); Lock(); #if defined(CHECK_FOR_DUPLICATE_LOCKED_POOL_RELEASE) && defined(DEBUG) { CLockedPoolItem *pTemp; pTemp = m_pPool; while ( pTemp != NULL ) { DNASSERT( pTemp != pItem ); pTemp = pTemp->GetNext(); } } #endif // CHECK_FOR_DUPLICATE_LOCKED_POOL_RELEASE DEBUG_ONLY( pItem->SetOwningPool( NULL ) ); #ifdef NO_POOLS pItem->PoolDeallocFunction(); delete pItem; #else pItem->LinkToPool( &m_pPool ); #endif Unlock(); DEBUG_ONLY( InterlockedDecrement( const_cast( &m_lOutstandingItemCount ) ) ); } //********************************************************************** // // class to act as a link in the pool // class CLockedTLPoolItem { public: #undef DPF_MODNAME #define DPF_MODNAME "CLockedTLPoolItem::CLockedTLPoolItem" CLockedTLPoolItem() { m_iRefCount = 0; m_blList.Initialize(); } #undef DPF_MODNAME #define DPF_MODNAME "CLockedTLPoolItem::~CLockedTLPoolItem" virtual ~CLockedTLPoolItem() { DNASSERT( m_iRefCount == 0 ); DNASSERT( m_blList.IsEmpty() ); } #undef DPF_MODNAME #define DPF_MODNAME "CLockedTLPoolItem::AddRef" void AddRef( void ) { DNASSERT( m_iRefCount != -1 ); InterlockedIncrement( const_cast( &m_iRefCount ) ); } #undef DPF_MODNAME #define DPF_MODNAME "CLockedTLPoolItem::DecRef" void DecRef( void ) { DNASSERT( m_iRefCount != 0 ); if ( InterlockedDecrement( const_cast( &m_iRefCount ) ) == 0 ) { ReturnSelfToPool(); } } // // Default initialization and deinitialization functions. These can // be overridden by the derived classes. // virtual BOOL PoolAllocFunction( void ){ return TRUE; } virtual BOOL PoolInitFunction( void ){ return TRUE; } virtual void PoolReleaseFunction( void ){}; virtual void PoolDeallocFunction( void ){}; // // linkage to the rest of the pool // CBilink m_blList; #ifdef TRACK_POOL_STATS DWORD m_dwSourceThreadID; #endif // TRACK_POOL_STATS protected: private: // // reference count used to return this item to the pool // volatile LONG m_iRefCount; virtual void ReturnSelfToPool( void ) = 0; // // prevent unwarranted copies // CLockedTLPoolItem( const CLockedTLPoolItem & ); CLockedTLPoolItem& operator=( const CLockedTLPoolItem & ); }; // // class to manage the pool // template< class T > class CLockedTLPool { public: CLockedTLPool(); ~CLockedTLPool(); BOOL Initialize( CLockedTLPool< T > * pGlobalPool ); void Deinitialize( void ); T *Get( void ); void Release( T *const pItem ); static void ReleaseWithoutPool( T *const pItem ); protected: private: CBilink m_blItems; // bilink list of available elements CLockedTLPool< T > * m_pGlobalPool; // pointer to global pool, or NULL if this is the global pool DNCRITICAL_SECTION * m_pcsLock; // pointer to pool critical section, or NULL if this is not the global pool DWORD m_dwNumItems; // number of items currently in this pool DEBUG_ONLY( BOOL m_fInitialized ); #ifdef TRACK_POOL_STATS DWORD m_dwAllocations; // how many objects were allocated by this pool DWORD m_dwSlurpsFromGlobal; // how many times some items were taken from the global pool DWORD m_dwLargestSlurpFromGlobal; // most number of items taken from the global pool at one time DWORD m_dwRetrievals; // how many times objects were pulled out of this pool DWORD m_dwReturnsOnSameThread; // how many times objects that were pulled out of this pool were returned to this pool DWORD m_dwReturnsOnDifferentThread; // how many times objects that were pulled out of another thread's pool were returned to this pool DWORD m_dwDumpsToGlobal; // how many times some items were dumped into the global pool DWORD m_dwDeallocations; // how many objects were freed by this pool #endif // TRACK_POOL_STATS // // prevent unwarranted copies // CLockedTLPool< T >( const CLockedTLPool< T > & ); CLockedTLPool< T >& operator=( const CLockedTLPool< T > & ); }; //********************************************************************** // Class function definitions //********************************************************************** //********************************************************************** // ------------------------------ // CLockedTLPool::CLockedTLPool - constructor // // Entry: Nothing // // Exit: Nothing // ------------------------------ #undef DPF_MODNAME #define DPF_MODNAME "CLockedTLPool::CLockedTLPool" template< class T > CLockedTLPool< T >::CLockedTLPool(): m_pGlobalPool( NULL ), m_pcsLock( NULL ), m_dwNumItems( 0 ) { m_blItems.Initialize(); DEBUG_ONLY( m_fInitialized = FALSE ); #ifdef TRACK_POOL_STATS m_dwAllocations = 0; m_dwSlurpsFromGlobal = 0; m_dwLargestSlurpFromGlobal = 0; m_dwRetrievals = 0; m_dwReturnsOnSameThread = 0; m_dwReturnsOnDifferentThread = 0; m_dwDumpsToGlobal = 0; m_dwDeallocations = 0; #endif // TRACK_POOL_STATS } //********************************************************************** //********************************************************************** // ------------------------------ // CLockedTLPool::~CLockedTLPool - destructor // // Entry: Nothing // // Exit: Nothing // ------------------------------ #undef DPF_MODNAME #define DPF_MODNAME "CLockedTLPool::~CLockedTLPool" template< class T > CLockedTLPool< T >::~CLockedTLPool() { DNASSERT( m_fInitialized == FALSE ); DNASSERT( m_dwNumItems == 0 ); DNASSERT( m_blItems.IsEmpty() ); DNASSERT( m_pcsLock == NULL ); #ifdef TRACK_POOL_STATS DPFX(DPFPREP, 9, "Pool 0x%p information:", this); DPFX(DPFPREP, 9, "\tAllocations = %u", m_dwAllocations); DPFX(DPFPREP, 9, "\tSlurpsFromGlobal = %u", m_dwSlurpsFromGlobal); DPFX(DPFPREP, 9, "\tLargestSlurpFromGlobal = %u", m_dwLargestSlurpFromGlobal); DPFX(DPFPREP, 9, "\tRetrievals = %u", m_dwRetrievals); DPFX(DPFPREP, 9, "\tReturnsOnSameThread = %u", m_dwReturnsOnSameThread); DPFX(DPFPREP, 9, "\tReturnsOnDifferentThread = %u", m_dwReturnsOnDifferentThread); DPFX(DPFPREP, 9, "\tDumpsToGlobal = %u", m_dwDumpsToGlobal); DPFX(DPFPREP, 9, "\tDeallocations = %u", m_dwDeallocations); #endif // TRACK_POOL_STATS } //********************************************************************** //********************************************************************** // ------------------------------ // CLockedTLPool::Initialize - initialize pool // // Entry: Pointer to global pool, or NULL if this is the global pool // // Exit: Boolean indicating success // TRUE = initialization succeeded // FALSE = initialization failed // ------------------------------ #undef DPF_MODNAME #define DPF_MODNAME "CLockedTLPool::Initialize" template< class T > BOOL CLockedTLPool< T >::Initialize( CLockedTLPool< T > * pGlobalPool ) { BOOL fReturn; fReturn = TRUE; m_pGlobalPool = pGlobalPool; if (pGlobalPool == NULL) { //DPFX(DPFPREP, 9, "Initializing global pool 0x%p.", this); m_pcsLock = (DNCRITICAL_SECTION*) DNMalloc( sizeof(DNCRITICAL_SECTION) ); if (m_pcsLock == NULL) { fReturn = FALSE; goto Exit; } if (! DNInitializeCriticalSection( m_pcsLock )) { DNFree( m_pcsLock ); DEBUG_ONLY( m_pcsLock = NULL ); fReturn = FALSE; goto Exit; } } DEBUG_ONLY( DNASSERT( m_fInitialized == FALSE ) ); DEBUG_ONLY( m_fInitialized = TRUE ); Exit: return fReturn; } //********************************************************************** //********************************************************************** // ------------------------------ // CLockedTLPool::Deinitialize - deinitialize pool // // Entry: Nothing // // Exit: Nothing // ------------------------------ #undef DPF_MODNAME #define DPF_MODNAME "CLockedTLPool::Deinitialize" template< class T > void CLockedTLPool< T >::Deinitialize( void ) { CBilink * pBilink; T * pItem; pBilink = m_blItems.GetNext(); while ( pBilink != &m_blItems ) { pItem = CONTAINING_OBJECT(pBilink, T, m_blList); pBilink = pBilink->GetNext(); pItem->m_blList.RemoveFromList(); pItem->PoolDeallocFunction(); delete pItem; #ifdef TRACK_POOL_STATS m_dwDeallocations++; #endif // TRACK_POOL_STATS DEBUG_ONLY( m_dwNumItems-- ); } DEBUG_ONLY( DNASSERT( m_fInitialized != FALSE ) ); DEBUG_ONLY( m_fInitialized = FALSE ); if (m_pcsLock != NULL) { //DPFX(DPFPREP, 9, "Deinitializing global pool 0x%p.", this); DNDeleteCriticalSection( m_pcsLock ); DNFree( m_pcsLock ); DEBUG_ONLY( m_pcsLock = NULL ); } } //********************************************************************** //********************************************************************** // ------------------------------ // CLockedTLPool::Get - get an item from the pool // // Entry: Nothing // // Exit: Pointer to item // NULL = out of memory // ------------------------------ #undef DPF_MODNAME #define DPF_MODNAME "CLockedTLPool::Get" template< class T > T *CLockedTLPool< T >::Get( void ) { CBilink * pBilink; T * pReturn; DNASSERT( m_fInitialized != FALSE ); DNASSERT( m_pGlobalPool != NULL ); // // If the pool is empty, steal the ones in the global pool. // pBilink = m_blItems.GetNext(); if ( pBilink == &m_blItems ) { DNEnterCriticalSection( m_pGlobalPool->m_pcsLock ); pBilink = m_pGlobalPool->m_blItems.GetNext(); if ( pBilink == &m_pGlobalPool->m_blItems ) { // // No items. Drop global pool lock and allocate a new one. // DNLeaveCriticalSection( m_pGlobalPool->m_pcsLock ); pReturn = new T; if ( pReturn != NULL ) { if ( pReturn->PoolAllocFunction() == FALSE ) { delete pReturn; pReturn = NULL; goto Exit; } #ifdef TRACK_POOL_STATS else { // // Update counter. // m_dwAllocations++; } #endif // TRACK_POOL_STATS } else { pReturn = NULL; goto Exit; } } else { // // Separate all the items from the global list. // We still have a pointer to the orphaned items (pBilink). // m_pGlobalPool->m_blItems.RemoveFromList(); DNASSERT(m_pGlobalPool->m_dwNumItems > 0); #ifdef TRACK_POOL_STATS m_dwSlurpsFromGlobal++; if ( m_pGlobalPool->m_dwNumItems > m_dwLargestSlurpFromGlobal ) { m_dwLargestSlurpFromGlobal = m_pGlobalPool->m_dwNumItems; } #endif // TRACK_POOL_STATS m_dwNumItems = m_pGlobalPool->m_dwNumItems - 1; // -1 because we need one right now m_pGlobalPool->m_dwNumItems = 0; // // Drop the lock since we don't need the global pool anymore. // DNLeaveCriticalSection( m_pGlobalPool->m_pcsLock ); // // Get the first item from the orphaned list. // pReturn = CONTAINING_OBJECT(pBilink, T, m_blList); // // If there was more than one item in the global pool, transfer // the remaining orphaned items (after the first) to this pool. // if (pBilink != pBilink->GetNext()) { pBilink = pBilink->GetNext(); pReturn->m_blList.RemoveFromList(); m_blItems.InsertBefore(pBilink); } } } else { pReturn = CONTAINING_OBJECT(pBilink, T, m_blList); pBilink->RemoveFromList(); DNASSERT( m_dwNumItems > 0 ); m_dwNumItems--; } // // If we're here, we have an entry (it was freshly created, or removed from // some pool). Attempt to initialize it before passing it to the user. // if ( pReturn->PoolInitFunction() == FALSE ) { pReturn->m_blList.InsertAfter( &m_blItems ); pReturn = NULL; } else { pReturn->AddRef(); #ifdef TRACK_POOL_STATS // // Update status counts. // m_dwRetrievals++; pReturn->m_dwSourceThreadID = GetCurrentThreadId(); #endif // TRACK_POOL_STATS } Exit: return pReturn; } //********************************************************************** //********************************************************************** // ------------------------------ // CLockedTLPool::Release - return item to pool // // Entry: Pointer to item // // Exit: Nothing // ------------------------------ #undef DPF_MODNAME #define DPF_MODNAME "CLockedTLPool::Release" template< class T > void CLockedTLPool< T >::Release( T *const pItem ) { DNASSERT( m_fInitialized != FALSE ); DNASSERT( m_pGlobalPool != NULL ); DNASSERT( pItem != NULL ); DBG_CASSERT( sizeof( BYTE* ) == sizeof( pItem ) ); #if defined(CHECK_FOR_DUPLICATE_CONTEXTCFPM_RELEASE) && defined(DEBUG) DNASSERT( ! pItem->m_blList.IsListMember( &m_blItems )); #endif // CHECK_FOR_DUPLICATE_CONTEXTCFPM_RELEASE pItem->PoolReleaseFunction(); #ifdef TRACK_POOL_STATS // // Update status counts. // if (pItem->m_dwSourceThreadID == GetCurrentThreadId()) { m_dwReturnsOnSameThread++; } else { m_dwReturnsOnDifferentThread++; } #endif // TRACK_POOL_STATS #ifdef NO_POOLS pItem->PoolDeallocFunction(); delete pItem; #ifdef TRACK_POOL_STATS m_dwDeallocations++; #endif // TRACK_POOL_STATS #else pItem->m_blList.InsertAfter( &m_blItems ); m_dwNumItems++; // // If this pool has built up some extra items, return them to the // global pool. // if ( m_dwNumItems >= 25 ) { CBilink * pFirstItem; // // Save a pointer to the first item. // pFirstItem = m_blItems.GetNext(); DNASSERT( pFirstItem != &m_blItems ); // // Orphan the items. // m_blItems.RemoveFromList(); // // Take the lock and transfer the list to the global pool. // DNEnterCriticalSection( m_pGlobalPool->m_pcsLock ); m_pGlobalPool->m_blItems.AttachListBefore(pFirstItem); m_pGlobalPool->m_dwNumItems += m_dwNumItems; DNLeaveCriticalSection( m_pGlobalPool->m_pcsLock ); m_dwNumItems = 0; #ifdef TRACK_POOL_STATS m_dwDumpsToGlobal++; #endif // TRACK_POOL_STATS } #endif // ! NO_POOLS } //********************************************************************** //********************************************************************** // ------------------------------ // CLockedTLPool::ReleaseWithoutPool - destroy an item without returning it // to a pool // NOTE: this is a static function // and cannot use the 'this' pointer! // // Entry: Pointer to item // // Exit: Nothing // ------------------------------ #undef DPF_MODNAME #define DPF_MODNAME "CContextFixedTLPool::ReleaseWithoutPool" template< class T > void CLockedTLPool< T >::ReleaseWithoutPool( T *const pItem ) { DNASSERT( pItem != NULL ); DBG_CASSERT( sizeof( BYTE* ) == sizeof( pItem ) ); DNASSERT( pItem->m_blList.IsEmpty() ); pItem->PoolReleaseFunction(); pItem->PoolDeallocFunction(); delete pItem; } //********************************************************************** #ifdef __LOCAL_OFFSETOF_DEFINED__ #undef __LOCAL_OFFSETOF_DEFINED__ #undef OFFSETOF #endif // __LOCAL_OFFSETOF_DEFINED__ #undef DPF_SUBCOMP #undef DPF_MODNAME #endif // __LOCKED_POOL__