1 // 2 // Copyright (C) 2002-2005 3Dlabs Inc. Ltd. 3 // All rights reserved. 4 // 5 // Redistribution and use in source and binary forms, with or without 6 // modification, are permitted provided that the following conditions 7 // are met: 8 // 9 // Redistributions of source code must retain the above copyright 10 // notice, this list of conditions and the following disclaimer. 11 // 12 // Redistributions in binary form must reproduce the above 13 // copyright notice, this list of conditions and the following 14 // disclaimer in the documentation and/or other materials provided 15 // with the distribution. 16 // 17 // Neither the name of 3Dlabs Inc. Ltd. nor the names of its 18 // contributors may be used to endorse or promote products derived 19 // from this software without specific prior written permission. 20 // 21 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 24 // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 25 // COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 26 // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 27 // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 28 // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 29 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 31 // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 32 // POSSIBILITY OF SUCH DAMAGE. 33 // 34 35 #include "../Include/Common.h" 36 #include "../Include/PoolAlloc.h" 37 38 #include "../Include/InitializeGlobals.h" 39 #include "../OSDependent/osinclude.h" 40 41 namespace glslang { 42 43 OS_TLSIndex PoolIndex; 44 45 void InitializeMemoryPools() 46 { 47 TThreadMemoryPools* pools = static_cast<TThreadMemoryPools*>(OS_GetTLSValue(PoolIndex)); 48 if (pools) 49 return; 50 51 TPoolAllocator *threadPoolAllocator = new TPoolAllocator(); 52 53 TThreadMemoryPools* threadData = new TThreadMemoryPools(); 54 55 threadData->threadPoolAllocator = threadPoolAllocator; 56 57 OS_SetTLSValue(PoolIndex, threadData); 58 } 59 60 void FreeGlobalPools() 61 { 62 // Release the allocated memory for this thread. 63 TThreadMemoryPools* globalPools = static_cast<TThreadMemoryPools*>(OS_GetTLSValue(PoolIndex)); 64 if (! globalPools) 65 return; 66 67 GetThreadPoolAllocator().popAll(); 68 delete &GetThreadPoolAllocator(); 69 delete globalPools; 70 } 71 72 bool InitializePoolIndex() 73 { 74 // Allocate a TLS index. 75 if ((PoolIndex = OS_AllocTLSIndex()) == OS_INVALID_TLS_INDEX) 76 return false; 77 78 return true; 79 } 80 81 void FreePoolIndex() 82 { 83 // Release the TLS index. 84 OS_FreeTLSIndex(PoolIndex); 85 } 86 87 TPoolAllocator& GetThreadPoolAllocator() 88 { 89 TThreadMemoryPools* threadData = static_cast<TThreadMemoryPools*>(OS_GetTLSValue(PoolIndex)); 90 91 return *threadData->threadPoolAllocator; 92 } 93 94 void SetThreadPoolAllocator(TPoolAllocator& poolAllocator) 95 { 96 TThreadMemoryPools* threadData = static_cast<TThreadMemoryPools*>(OS_GetTLSValue(PoolIndex)); 97 98 threadData->threadPoolAllocator = &poolAllocator; 99 } 100 101 // 102 // Implement the functionality of the TPoolAllocator class, which 103 // is documented in PoolAlloc.h. 104 // 105 TPoolAllocator::TPoolAllocator(int growthIncrement, int allocationAlignment) : 106 pageSize(growthIncrement), 107 alignment(allocationAlignment), 108 freeList(nullptr), 109 inUseList(nullptr), 110 numCalls(0) 111 { 112 // 113 // Don't allow page sizes we know are smaller than all common 114 // OS page sizes. 115 // 116 if (pageSize < 4*1024) 117 pageSize = 4*1024; 118 119 // 120 // A large currentPageOffset indicates a new page needs to 121 // be obtained to allocate memory. 122 // 123 currentPageOffset = pageSize; 124 125 // 126 // Adjust alignment to be at least pointer aligned and 127 // power of 2. 128 // 129 size_t minAlign = sizeof(void*); 130 alignment &= ~(minAlign - 1); 131 if (alignment < minAlign) 132 alignment = minAlign; 133 size_t a = 1; 134 while (a < alignment) 135 a <<= 1; 136 alignment = a; 137 alignmentMask = a - 1; 138 139 // 140 // Align header skip 141 // 142 headerSkip = minAlign; 143 if (headerSkip < sizeof(tHeader)) { 144 headerSkip = (sizeof(tHeader) + alignmentMask) & ~alignmentMask; 145 } 146 147 push(); 148 } 149 150 TPoolAllocator::~TPoolAllocator() 151 { 152 while (inUseList) { 153 tHeader* next = inUseList->nextPage; 154 inUseList->~tHeader(); 155 delete [] reinterpret_cast<char*>(inUseList); 156 inUseList = next; 157 } 158 159 // 160 // Always delete the free list memory - it can't be being 161 // (correctly) referenced, whether the pool allocator was 162 // global or not. We should not check the guard blocks 163 // here, because we did it already when the block was 164 // placed into the free list. 165 // 166 while (freeList) { 167 tHeader* next = freeList->nextPage; 168 delete [] reinterpret_cast<char*>(freeList); 169 freeList = next; 170 } 171 } 172 173 const unsigned char TAllocation::guardBlockBeginVal = 0xfb; 174 const unsigned char TAllocation::guardBlockEndVal = 0xfe; 175 const unsigned char TAllocation::userDataFill = 0xcd; 176 177 # ifdef GUARD_BLOCKS 178 const size_t TAllocation::guardBlockSize = 16; 179 # else 180 const size_t TAllocation::guardBlockSize = 0; 181 # endif 182 183 // 184 // Check a single guard block for damage 185 // 186 #ifdef GUARD_BLOCKS 187 void TAllocation::checkGuardBlock(unsigned char* blockMem, unsigned char val, const char* locText) const 188 #else 189 void TAllocation::checkGuardBlock(unsigned char*, unsigned char, const char*) const 190 #endif 191 { 192 #ifdef GUARD_BLOCKS 193 for (size_t x = 0; x < guardBlockSize; x++) { 194 if (blockMem[x] != val) { 195 const int maxSize = 80; 196 char assertMsg[maxSize]; 197 198 // We don't print the assert message. It's here just to be helpful. 199 snprintf(assertMsg, maxSize, "PoolAlloc: Damage %s %zu byte allocation at 0x%p\n", 200 locText, size, data()); 201 assert(0 && "PoolAlloc: Damage in guard block"); 202 } 203 } 204 #else 205 assert(guardBlockSize == 0); 206 #endif 207 } 208 209 void TPoolAllocator::push() 210 { 211 tAllocState state = { currentPageOffset, inUseList }; 212 213 stack.push_back(state); 214 215 // 216 // Indicate there is no current page to allocate from. 217 // 218 currentPageOffset = pageSize; 219 } 220 221 // 222 // Do a mass-deallocation of all the individual allocations 223 // that have occurred since the last push(), or since the 224 // last pop(), or since the object's creation. 225 // 226 // The deallocated pages are saved for future allocations. 227 // 228 void TPoolAllocator::pop() 229 { 230 if (stack.size() < 1) 231 return; 232 233 tHeader* page = stack.back().page; 234 currentPageOffset = stack.back().offset; 235 236 while (inUseList != page) { 237 // invoke destructor to free allocation list 238 inUseList->~tHeader(); 239 240 tHeader* nextInUse = inUseList->nextPage; 241 if (inUseList->pageCount > 1) 242 delete [] reinterpret_cast<char*>(inUseList); 243 else { 244 inUseList->nextPage = freeList; 245 freeList = inUseList; 246 } 247 inUseList = nextInUse; 248 } 249 250 stack.pop_back(); 251 } 252 253 // 254 // Do a mass-deallocation of all the individual allocations 255 // that have occurred. 256 // 257 void TPoolAllocator::popAll() 258 { 259 while (stack.size() > 0) 260 pop(); 261 } 262 263 void* TPoolAllocator::allocate(size_t numBytes) 264 { 265 // If we are using guard blocks, all allocations are bracketed by 266 // them: [guardblock][allocation][guardblock]. numBytes is how 267 // much memory the caller asked for. allocationSize is the total 268 // size including guard blocks. In release build, 269 // guardBlockSize=0 and this all gets optimized away. 270 size_t allocationSize = TAllocation::allocationSize(numBytes); 271 272 // 273 // Just keep some interesting statistics. 274 // 275 ++numCalls; 276 totalBytes += numBytes; 277 278 // 279 // Do the allocation, most likely case first, for efficiency. 280 // This step could be moved to be inline sometime. 281 // 282 if (currentPageOffset + allocationSize <= pageSize) { 283 // 284 // Safe to allocate from currentPageOffset. 285 // 286 unsigned char* memory = reinterpret_cast<unsigned char*>(inUseList) + currentPageOffset; 287 currentPageOffset += allocationSize; 288 currentPageOffset = (currentPageOffset + alignmentMask) & ~alignmentMask; 289 290 return initializeAllocation(inUseList, memory, numBytes); 291 } 292 293 if (allocationSize + headerSkip > pageSize) { 294 // 295 // Do a multi-page allocation. Don't mix these with the others. 296 // The OS is efficient and allocating and free-ing multiple pages. 297 // 298 size_t numBytesToAlloc = allocationSize + headerSkip; 299 tHeader* memory = reinterpret_cast<tHeader*>(::new char[numBytesToAlloc]); 300 if (memory == 0) 301 return 0; 302 303 // Use placement-new to initialize header 304 new(memory) tHeader(inUseList, (numBytesToAlloc + pageSize - 1) / pageSize); 305 inUseList = memory; 306 307 currentPageOffset = pageSize; // make next allocation come from a new page 308 309 // No guard blocks for multi-page allocations (yet) 310 return reinterpret_cast<void*>(reinterpret_cast<UINT_PTR>(memory) + headerSkip); 311 } 312 313 // 314 // Need a simple page to allocate from. 315 // 316 tHeader* memory; 317 if (freeList) { 318 memory = freeList; 319 freeList = freeList->nextPage; 320 } else { 321 memory = reinterpret_cast<tHeader*>(::new char[pageSize]); 322 if (memory == 0) 323 return 0; 324 } 325 326 // Use placement-new to initialize header 327 new(memory) tHeader(inUseList, 1); 328 inUseList = memory; 329 330 unsigned char* ret = reinterpret_cast<unsigned char*>(inUseList) + headerSkip; 331 currentPageOffset = (headerSkip + allocationSize + alignmentMask) & ~alignmentMask; 332 333 return initializeAllocation(inUseList, ret, numBytes); 334 } 335 336 // 337 // Check all allocations in a list for damage by calling check on each. 338 // 339 void TAllocation::checkAllocList() const 340 { 341 for (const TAllocation* alloc = this; alloc != 0; alloc = alloc->prevAlloc) 342 alloc->check(); 343 } 344 345 } // end namespace glslang 346