//+--------------------------------------------------------------------------- // // 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, dwFlags)); 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; }