NT4/private/ole32/olethunk/olethk32/freelist.cxx
2020-09-30 17:12:29 +02:00

469 lines
14 KiB
C++

//+---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1992 - 1994.
//
// File: freelist.cxx
//
// Contents: CFreeList implementations
//
// History: 07-Jul-94 BobDay Created
//
//----------------------------------------------------------------------------
#include "headers.cxx"
#pragma hdrstop
//
// Each element, when it is free, has a pointer stored within it that
// points to the next free element. We can do this because we know that
// the element is free, all of its data is unused. These pointers are used
// as DWORDs since they can be virtual pointers (16:16).
//
#define CALC_NEXTPTR(lpElement) \
((LPDWORD)((DWORD)(lpElement) + m_iNextPtrOffset))
//
// Each block of elements has a pointer to the next block of elements. We
// allocate extra room for this pointer just after all of the elements within
// the block. These pointers are used as DWORDs since they can be virtual
// pointers (16:16).
#define CALC_BLOCKNEXTPTR(lpBlock,dwElementSectionSize) \
((LPDWORD)((DWORD)(lpBlock) + (dwElementSectionSize)))
//
// Here are our global free lists, created on DLL load
// The block sizes are generally -1 to allow space for block
// list overhead
//
CFreeList flFreeList16( // THUNK1632OBJ free list
&mmodel16Public,
sizeof(THUNK1632OBJ),
63,
FIELD_OFFSET(THUNK1632OBJ, pphHolder));
CFreeList flFreeList32( // THUNK3216OBJ free list
&mmodel32,
sizeof(THUNK3216OBJ),
63,
FIELD_OFFSET(THUNK3216OBJ, pphHolder));
CFreeList flHolderFreeList( // PROXYHOLDER free list
&mmodel32,
sizeof(PROXYHOLDER),
63,
FIELD_OFFSET(PROXYHOLDER, pphNext));
CFreeList flRequestFreeList( // IID request free list
&mmodel32,
sizeof(IIDNODE),
7,
FIELD_OFFSET(IIDNODE, pNextNode));
//+---------------------------------------------------------------------------
//
// Method: CFreeList::CFreeList
//
// Arguments: pmm - Memory model to use
// iElementSize - The size of the structure being made into a
// free list. e.g. sizeof THUNK1632OBJ
// iElementsPerBlock - How many elements to allocate at a time
// (a block contains this many elements).
// iNextPtrOffset - Offset within the element's structure for
// the place to store the free list's next
// element pointer. Sometimes (for debugging,
// etc.) it is desirable to make this NOT 0
// (the beginning of the element structure).
//
// Synopsis: constructor for CFreeList class
//
// History: 6-01-94 JohannP (Johann Posch) Created
// 7-05-94 BobDay (Bob Day) Changed it to be list based
//
//----------------------------------------------------------------------------
CFreeList::CFreeList( CMemoryModel *pmm,
UINT iElementSize,
UINT iElementsPerBlock,
UINT iNextPtrOffset )
{
//
// Save away the allocator information
//
m_pmm = pmm;
m_iElementSize = iElementSize;
m_iElementsPerBlock = iElementsPerBlock;
m_iNextPtrOffset = iNextPtrOffset;
//
// Set the list of elements to empty
//
m_dwHeadElement = 0;
m_dwTailElement = 0;
//
// Set the list of blocks to empty
//
m_dwHeadBlock = 0;
}
//+---------------------------------------------------------------------------
//
// Method: CFreeList::AllocElement
//
// Synopsis: Allocates an element from the various blocks of elements
// and allocates a new block if necessary.
//
// Returns: 0 if failed to alloc an element,
// otherwise the DWORD representing the alloc'd element.
//
// History: 7-05-94 BobDay (Bob Day) Created
//
//----------------------------------------------------------------------------
DWORD CFreeList::AllocElement( void )
{
DWORD dwNewHeadBlock;
DWORD dwElementSectionSize;
DWORD dwBlockSize;
LPVOID lpBlock;
UINT iCnt;
DWORD dwElement;
LPVOID lpElement;
LPDWORD lpElementNextPtr;
//
// If the list of available elements is empty, callback to the derived
// class and make them add an entire new block of elements.
//
if ( m_dwHeadElement == 0 )
{
//
// Allocate a new block
//
iCnt = m_iElementsPerBlock;
dwElementSectionSize = m_iElementSize * m_iElementsPerBlock;
//
// Here we allocate an extra DWORD so that we can store in the block
// the address of the next block. In this way we have a list of
// blocks so that when the time comes to free them, we can find them
// all.
//
dwBlockSize = dwElementSectionSize + sizeof(DWORD);
dwNewHeadBlock = m_pmm->AllocMemory( dwBlockSize );
if ( dwNewHeadBlock == 0 )
{
//
// Yikes, the block allocator failed!
//
thkDebugOut((DEB_ERROR,
"CFreeList::AllocElement, AllocMemory failed\n"));
return 0;
}
//
// Now initialize the block and link it into the block list.
//
lpBlock = m_pmm->ResolvePtr( dwNewHeadBlock, dwBlockSize );
if ( lpBlock == NULL )
{
//
// Couldn't get a pointer to the block, some memory mapping
// problem?
//
thkDebugOut((DEB_ERROR,
"CFreeList::AllocElement, "
"ResolvePtr for block failed "
"for address %08lX, size %08lX\n",
dwNewHeadBlock, dwBlockSize ));
// Try to return bad block to pool
m_pmm->FreeMemory( dwNewHeadBlock );
return 0;
}
#if DBG == 1
// 0xDE = Alloc'd but not init'd
memset( lpBlock, 0xDE, dwBlockSize );
#endif
//
// Make this block point to the previous block
//
*CALC_BLOCKNEXTPTR(lpBlock,dwElementSectionSize) = m_dwHeadBlock;
m_dwHeadBlock = dwNewHeadBlock; // Update block list
m_pmm->ReleasePtr(dwNewHeadBlock);
//
// Now initialize all of the elements within the block to be free.
//
// The below loop skips the first element, free's all of the remaining
// ones. This way we can return the first one and all of the rest will
// be in accending order; The order doesn't really matter, but its
// nice.
//
dwElement = dwNewHeadBlock;
while ( iCnt > 1 ) // Free n-1 items (we skip the first)
{
--iCnt;
dwElement += m_iElementSize; // Skip to next one (miss 1st one)
FreeElement( dwElement );
}
dwElement = dwNewHeadBlock; // Use the first one as our alloc'd one
}
else
{
// We better have some blocks by now
thkAssert( m_dwHeadBlock != 0 );
// Better have a "end of list" too!
thkAssert( m_dwTailElement != 0 );
//
// Grab an available element off the top (head) of the list.
//
dwElement = m_dwHeadElement;
lpElement = m_pmm->ResolvePtr( dwElement, m_iElementSize );
if ( lpElement == NULL )
{
//
// Yikes, we weren't able to get a pointer to the element!
//
thkDebugOut((DEB_ERROR,
"CFreeList::AllocElement, "
"ResolvePtr for element failed "
"for address %08lX, size %08lX\n",
dwElement, m_iElementSize ));
return 0;
}
//
// Update the list to reflect the fact that we just removed the head
// and replace it with the one which was pointed to by the head.
//
lpElementNextPtr = CALC_NEXTPTR(lpElement);
m_dwHeadElement = *lpElementNextPtr;
m_pmm->ReleasePtr(dwElement);
//
// Also, if we are now at the end of the list, then the tail element
// should point to nowhere (i.e. there is nothing to insert after).
//
if ( m_dwHeadElement == 0 )
{
m_dwTailElement = 0;
}
}
#if DBG == 1
// Erase the memory being returned to highlight reuse of dead values
lpElement = m_pmm->ResolvePtr( dwElement, m_iElementSize );
memset( lpElement, 0xED, m_iElementSize );
m_pmm->ReleasePtr(dwElement);
thkDebugOut((DEB_ITRACE,
"CFreeList::AllocElement, allocated element at %08lX\n",
dwElement ));
#endif
return dwElement;
}
//+---------------------------------------------------------------------------
//
// Method: CFreeList::FreeElement
//
// Synopsis: Un-Allocates an element from the various blocks of elements,
// basically put the element back on the free list.
//
// Arguments: dwElement - Element to free
//
// Returns: -none- Asserts if failed.
//
// History: 7-05-94 BobDay (Bob Day) Created
//
//----------------------------------------------------------------------------
void CFreeList::FreeElement( DWORD dwElement )
{
LPVOID lpElement;
LPDWORD lpElementNextPtr;
DWORD dwResolved;
//
// First, make sure we can set this new element's next element pointer
// to zero (he's going to be a the end of the list).
//
lpElement = m_pmm->ResolvePtr( dwElement, m_iElementSize );
if ( lpElement == NULL )
{
//
// Yikes, we couldn't get a pointer to this element's place to store
// its next pointer.
//
thkDebugOut((DEB_ERROR,
"CFreeList::FreeElement, "
"ResolvePtr failed for free'd element\n"
"for address %08lX, size %08lX\n",
dwElement, m_iElementSize ));
thkAssert(FALSE && "CFreeList::FreeElement, "
"Resolve Ptr failed for free'd element\n");
return;
}
#if DBG == 1
// Fill memory so its values can't be reused
if ( fZapProxy ) // Not doing this is important for
// the "PrepareForCleanup" processing the OLE32
// does on thread detach. ZapProxy can be used
// to turn it back on.
{
memset(lpElement, 0xDD, m_iElementSize);
}
#endif
lpElementNextPtr = CALC_NEXTPTR(lpElement);
*lpElementNextPtr = 0; // Zap his next pointer since he'll be on the end
m_pmm->ReleasePtr(dwElement);
//
// Add this element back onto the end (tail) of the list.
//
if ( m_dwTailElement == 0 )
{
//
// Well, the list was empty, time to set it up
//
thkAssert( m_dwHeadElement == 0 );
lpElementNextPtr = &m_dwHeadElement;
dwResolved = 0;
}
else
{
//
// Ok, the list wasn't empty, so we add this new one onto the end.
//
thkAssert( m_dwHeadElement != 0 );
dwResolved = m_dwTailElement;
lpElement = m_pmm->ResolvePtr( m_dwTailElement, m_iElementSize );
if ( lpElement == NULL )
{
//
// Oh no, we couldn't get a pointer to the next element pointer for
// the guy who is currently the tail of the list.
//
thkDebugOut((DEB_ERROR,
"CFreeList::FreeElement, "
"ResolvePtr failed for last element\n"
"for address %08lX, size %08lX\n",
m_dwTailElement, m_iElementSize ));
thkAssert(FALSE && "CFreeList::FreeElement, "
"Resolve Ptr failed for last element\n");
return;
}
lpElementNextPtr = CALC_NEXTPTR(lpElement);
}
//
// Update our tail pointer to point to our newly free'd guy.
//
m_dwTailElement = dwElement;
//
// Make the last guy point to this newly free'd guy
//
*lpElementNextPtr = dwElement;
if (dwResolved != 0)
{
m_pmm->ReleasePtr(dwResolved);
}
thkDebugOut((DEB_ITRACE,
"CFreeList::FreeElement, free'd element at %08lX\n",
dwElement ));
}
//+---------------------------------------------------------------------------
//
// Method: CFreeList::FreeMemoryBlocks
//
// Arguments: -none-
//
// Returns: -nothing-
//
// Synopsis: Called by derived destructors to allow them to free up their
// contents before going away.
//
// History: 7-05-94 BobDay (Bob Day) Created it
//
//----------------------------------------------------------------------------
void CFreeList::FreeMemoryBlocks( void )
{
DWORD dwBlock;
DWORD dwElementSectionSize;
DWORD dwBlockSize;
DWORD dwNextBlock;
LPVOID lpBlock;
//
// Compute some constants for this list ahead of time
//
dwElementSectionSize = m_iElementSize * m_iElementsPerBlock;
//
// Add room for that extra DWORD, block next pointer. (See comment in
// AllocElement where it allocates an extra DWORD)
//
dwBlockSize = dwElementSectionSize + sizeof(DWORD);
//
// Iterate through the list of blocks free'ing them
//
dwBlock = m_dwHeadBlock;
while( dwBlock != 0 )
{
//
// Find the next block ptr
//
lpBlock = m_pmm->ResolvePtr( dwBlock, dwBlockSize );
if ( lpBlock == NULL )
{
//
// If we get an error here, we just drop out of loop
//
dwNextBlock = 0;
}
else
{
dwNextBlock = *CALC_BLOCKNEXTPTR(lpBlock,dwElementSectionSize);
#if DBG == 1
memset(lpBlock, 0xEE, dwBlockSize);
#endif
m_pmm->ReleasePtr(dwBlock);
m_pmm->FreeMemory( dwBlock );
}
dwBlock = dwNextBlock;
}
m_dwHeadElement = 0;
m_dwTailElement = 0;
m_dwHeadBlock = 0;
}