// //Copyright (C) 2002-2005 3Dlabs Inc. Ltd. //All rights reserved. // //Redistribution and use in source and binary forms, with or without //modification, are permitted provided that the following conditions //are met: // // Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // // Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following // disclaimer in the documentation and/or other materials provided // with the distribution. // // Neither the name of 3Dlabs Inc. Ltd. nor the names of its // contributors may be used to endorse or promote products derived // from this software without specific prior written permission. // //THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS //"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT //LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS //FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE //COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, //INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, //BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; //LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER //CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT //LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN //ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE //POSSIBILITY OF SUCH DAMAGE. // #include "../Include/PoolAlloc.h" #include "../Include/Common.h" #include "Include/InitializeGlobals.h" #include "osinclude.h" OS_TLSIndex PoolIndex; void InitializeGlobalPools() { TThreadGlobalPools* globalPools= static_cast(OS_GetTLSValue(PoolIndex)); if (globalPools) return; TPoolAllocator *globalPoolAllocator = new TPoolAllocator(true); TThreadGlobalPools* threadData = new TThreadGlobalPools(); threadData->globalPoolAllocator = globalPoolAllocator; OS_SetTLSValue(PoolIndex, threadData); globalPoolAllocator->push(); } void FreeGlobalPools() { // Release the allocated memory for this thread. TThreadGlobalPools* globalPools= static_cast(OS_GetTLSValue(PoolIndex)); if (!globalPools) return; GlobalPoolAllocator.popAll(); delete &GlobalPoolAllocator; delete globalPools; } bool InitializePoolIndex() { // Allocate a TLS index. if ((PoolIndex = OS_AllocTLSIndex()) == OS_INVALID_TLS_INDEX) return false; return true; } void FreePoolIndex() { // Release the TLS index. OS_FreeTLSIndex(PoolIndex); } TPoolAllocator& GetGlobalPoolAllocator() { TThreadGlobalPools* threadData = static_cast(OS_GetTLSValue(PoolIndex)); return *threadData->globalPoolAllocator; } void SetGlobalPoolAllocatorPtr(TPoolAllocator* poolAllocator) { TThreadGlobalPools* threadData = static_cast(OS_GetTLSValue(PoolIndex)); threadData->globalPoolAllocator = poolAllocator; } // // Implement the functionality of the TPoolAllocator class, which // is documented in PoolAlloc.h. // TPoolAllocator::TPoolAllocator(bool g, int growthIncrement, int allocationAlignment) : global(g), pageSize(growthIncrement), alignment(allocationAlignment), freeList(0), inUseList(0), numCalls(0) { // // Don't allow page sizes we know are smaller than all common // OS page sizes. // if (pageSize < 4*1024) pageSize = 4*1024; // // A large currentPageOffset indicates a new page needs to // be obtained to allocate memory. // currentPageOffset = pageSize; // // Adjust alignment to be at least pointer aligned and // power of 2. // size_t minAlign = sizeof(void*); alignment &= ~(minAlign - 1); if (alignment < minAlign) alignment = minAlign; size_t a = 1; while (a < alignment) a <<= 1; alignment = a; alignmentMask = a - 1; // // Align header skip // headerSkip = minAlign; if (headerSkip < sizeof(tHeader)) { headerSkip = (sizeof(tHeader) + alignmentMask) & ~alignmentMask; } // // Put a marker at the beginning of the stack. We won't // pop() past this. // tAllocState start = { currentPageOffset, 0 }; stack.push_back(start); } TPoolAllocator::~TPoolAllocator() { if (!global) { // // Then we know that this object is not being // allocated after other, globally scoped objects // that depend on it. So we can delete the "in use" memory. // while (inUseList) { tHeader* next = inUseList->nextPage; inUseList->~tHeader(); delete [] reinterpret_cast(inUseList); inUseList = next; } } // // Always delete the free list memory - it can't be being // (correctly) referenced, whether the pool allocator was // global or not. We should not check the guard blocks // here, because we did it already when the block was // placed into the free list. // while (freeList) { tHeader* next = freeList->nextPage; delete [] reinterpret_cast(freeList); freeList = next; } } // Support MSVC++ 6.0 const unsigned char TAllocation::guardBlockBeginVal = 0xfb; const unsigned char TAllocation::guardBlockEndVal = 0xfe; const unsigned char TAllocation::userDataFill = 0xcd; # ifdef GUARD_BLOCKS const size_t TAllocation::guardBlockSize = 16; # else const size_t TAllocation::guardBlockSize = 0; # endif // // Check a single guard block for damage // void TAllocation::checkGuardBlock(unsigned char* blockMem, unsigned char val, char* locText) const { for (int x = 0; x < guardBlockSize; x++) { if (blockMem[x] != val) { char assertMsg[80]; // We don't print the assert message. It's here just to be helpful. sprintf(assertMsg, "PoolAlloc: Damage %s %u byte allocation at 0x%p\n", locText, size, data()); assert(0 && "PoolAlloc: Damage in guard block"); } } } void TPoolAllocator::push() { tAllocState state = { currentPageOffset, inUseList }; stack.push_back(state); // // Indicate there is no current page to allocate from. // currentPageOffset = pageSize; } // // Do a mass-deallocation of all the individual allocations // that have occurred since the last push(), or since the // last pop(), or since the object's creation. // // The deallocated pages are saved for future allocations. // void TPoolAllocator::pop() { if (stack.size() < 1) return; tHeader* page = stack.back().page; currentPageOffset = stack.back().offset; while (inUseList != page) { // invoke destructor to free allocation list inUseList->~tHeader(); tHeader* nextInUse = inUseList->nextPage; if (inUseList->pageCount > 1) delete [] reinterpret_cast(inUseList); else { inUseList->nextPage = freeList; freeList = inUseList; } inUseList = nextInUse; } stack.pop_back(); } // // Do a mass-deallocation of all the individual allocations // that have occurred. // void TPoolAllocator::popAll() { while (stack.size() > 0) pop(); } void* TPoolAllocator::allocate(size_t numBytes) { // If we are using guard blocks, all allocations are bracketed by // them: [guardblock][allocation][guardblock]. numBytes is how // much memory the caller asked for. allocationSize is the total // size including guard blocks. In release build, // guardBlockSize=0 and this all gets optimized away. size_t allocationSize = TAllocation::allocationSize(numBytes); // // Just keep some interesting statistics. // ++numCalls; totalBytes += numBytes; // // Do the allocation, most likely case first, for efficiency. // This step could be moved to be inline sometime. // if (currentPageOffset + allocationSize <= pageSize) { // // Safe to allocate from currentPageOffset. // unsigned char* memory = reinterpret_cast(inUseList) + currentPageOffset; currentPageOffset += allocationSize; currentPageOffset = (currentPageOffset + alignmentMask) & ~alignmentMask; return initializeAllocation(inUseList, memory, numBytes); } if (allocationSize + headerSkip > pageSize) { // // Do a multi-page allocation. Don't mix these with the others. // The OS is efficient and allocating and free-ing multiple pages. // size_t numBytesToAlloc = allocationSize + headerSkip; tHeader* memory = reinterpret_cast(::new char[numBytesToAlloc]); if (memory == 0) return 0; // Use placement-new to initialize header new(memory) tHeader(inUseList, (numBytesToAlloc + pageSize - 1) / pageSize); inUseList = memory; currentPageOffset = pageSize; // make next allocation come from a new page // No guard blocks for multi-page allocations (yet) return reinterpret_cast(reinterpret_cast(memory) + headerSkip); } // // Need a simple page to allocate from. // tHeader* memory; if (freeList) { memory = freeList; freeList = freeList->nextPage; } else { memory = reinterpret_cast(::new char[pageSize]); if (memory == 0) return 0; } // Use placement-new to initialize header new(memory) tHeader(inUseList, 1); inUseList = memory; unsigned char* ret = reinterpret_cast(inUseList) + headerSkip; currentPageOffset = (headerSkip + allocationSize + alignmentMask) & ~alignmentMask; return initializeAllocation(inUseList, ret, numBytes); } // // Check all allocations in a list for damage by calling check on each. // void TAllocation::checkAllocList() const { for (const TAllocation* alloc = this; alloc != 0; alloc = alloc->prevAlloc) alloc->check(); }