PoolAlloc.cpp 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
  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. #include "../Include/Common.h"
  35. #include "../Include/PoolAlloc.h"
  36. #include "../Include/InitializeGlobals.h"
  37. #include "../OSDependent/osinclude.h"
  38. namespace glslang {
  39. // Process-wide TLS index
  40. OS_TLSIndex PoolIndex;
  41. // Return the thread-specific current pool.
  42. TPoolAllocator& GetThreadPoolAllocator()
  43. {
  44. return *static_cast<TPoolAllocator*>(OS_GetTLSValue(PoolIndex));
  45. }
  46. // Set the thread-specific current pool.
  47. void SetThreadPoolAllocator(TPoolAllocator* poolAllocator)
  48. {
  49. OS_SetTLSValue(PoolIndex, poolAllocator);
  50. }
  51. // Process-wide set up of the TLS pool storage.
  52. bool InitializePoolIndex()
  53. {
  54. // Allocate a TLS index.
  55. if ((PoolIndex = OS_AllocTLSIndex()) == OS_INVALID_TLS_INDEX)
  56. return false;
  57. return true;
  58. }
  59. //
  60. // Implement the functionality of the TPoolAllocator class, which
  61. // is documented in PoolAlloc.h.
  62. //
  63. TPoolAllocator::TPoolAllocator(int growthIncrement, int allocationAlignment) :
  64. pageSize(growthIncrement),
  65. alignment(allocationAlignment),
  66. freeList(nullptr),
  67. inUseList(nullptr),
  68. numCalls(0)
  69. {
  70. //
  71. // Don't allow page sizes we know are smaller than all common
  72. // OS page sizes.
  73. //
  74. if (pageSize < 4*1024)
  75. pageSize = 4*1024;
  76. //
  77. // A large currentPageOffset indicates a new page needs to
  78. // be obtained to allocate memory.
  79. //
  80. currentPageOffset = pageSize;
  81. //
  82. // Adjust alignment to be at least pointer aligned and
  83. // power of 2.
  84. //
  85. size_t minAlign = sizeof(void*);
  86. alignment &= ~(minAlign - 1);
  87. if (alignment < minAlign)
  88. alignment = minAlign;
  89. size_t a = 1;
  90. while (a < alignment)
  91. a <<= 1;
  92. alignment = a;
  93. alignmentMask = a - 1;
  94. //
  95. // Align header skip
  96. //
  97. headerSkip = minAlign;
  98. if (headerSkip < sizeof(tHeader)) {
  99. headerSkip = (sizeof(tHeader) + alignmentMask) & ~alignmentMask;
  100. }
  101. push();
  102. }
  103. TPoolAllocator::~TPoolAllocator()
  104. {
  105. while (inUseList) {
  106. tHeader* next = inUseList->nextPage;
  107. inUseList->~tHeader();
  108. delete [] reinterpret_cast<char*>(inUseList);
  109. inUseList = next;
  110. }
  111. //
  112. // Always delete the free list memory - it can't be being
  113. // (correctly) referenced, whether the pool allocator was
  114. // global or not. We should not check the guard blocks
  115. // here, because we did it already when the block was
  116. // placed into the free list.
  117. //
  118. while (freeList) {
  119. tHeader* next = freeList->nextPage;
  120. delete [] reinterpret_cast<char*>(freeList);
  121. freeList = next;
  122. }
  123. }
  124. const unsigned char TAllocation::guardBlockBeginVal = 0xfb;
  125. const unsigned char TAllocation::guardBlockEndVal = 0xfe;
  126. const unsigned char TAllocation::userDataFill = 0xcd;
  127. # ifdef GUARD_BLOCKS
  128. const size_t TAllocation::guardBlockSize = 16;
  129. # else
  130. const size_t TAllocation::guardBlockSize = 0;
  131. # endif
  132. //
  133. // Check a single guard block for damage
  134. //
  135. #ifdef GUARD_BLOCKS
  136. void TAllocation::checkGuardBlock(unsigned char* blockMem, unsigned char val, const char* locText) const
  137. #else
  138. void TAllocation::checkGuardBlock(unsigned char*, unsigned char, const char*) const
  139. #endif
  140. {
  141. #ifdef GUARD_BLOCKS
  142. for (size_t x = 0; x < guardBlockSize; x++) {
  143. if (blockMem[x] != val) {
  144. const int maxSize = 80;
  145. char assertMsg[maxSize];
  146. // We don't print the assert message. It's here just to be helpful.
  147. snprintf(assertMsg, maxSize, "PoolAlloc: Damage %s %zu byte allocation at 0x%p\n",
  148. locText, size, data());
  149. assert(0 && "PoolAlloc: Damage in guard block");
  150. }
  151. }
  152. #else
  153. assert(guardBlockSize == 0);
  154. #endif
  155. }
  156. void TPoolAllocator::push()
  157. {
  158. tAllocState state = { currentPageOffset, inUseList };
  159. stack.push_back(state);
  160. //
  161. // Indicate there is no current page to allocate from.
  162. //
  163. currentPageOffset = pageSize;
  164. }
  165. //
  166. // Do a mass-deallocation of all the individual allocations
  167. // that have occurred since the last push(), or since the
  168. // last pop(), or since the object's creation.
  169. //
  170. // The deallocated pages are saved for future allocations.
  171. //
  172. void TPoolAllocator::pop()
  173. {
  174. if (stack.size() < 1)
  175. return;
  176. tHeader* page = stack.back().page;
  177. currentPageOffset = stack.back().offset;
  178. while (inUseList != page) {
  179. tHeader* nextInUse = inUseList->nextPage;
  180. size_t pageCount = inUseList->pageCount;
  181. // This technically ends the lifetime of the header as C++ object,
  182. // but we will still control the memory and reuse it.
  183. inUseList->~tHeader(); // currently, just a debug allocation checker
  184. if (pageCount > 1) {
  185. delete [] reinterpret_cast<char*>(inUseList);
  186. } else {
  187. inUseList->nextPage = freeList;
  188. freeList = inUseList;
  189. }
  190. inUseList = nextInUse;
  191. }
  192. stack.pop_back();
  193. }
  194. //
  195. // Do a mass-deallocation of all the individual allocations
  196. // that have occurred.
  197. //
  198. void TPoolAllocator::popAll()
  199. {
  200. while (stack.size() > 0)
  201. pop();
  202. }
  203. void* TPoolAllocator::allocate(size_t numBytes)
  204. {
  205. // If we are using guard blocks, all allocations are bracketed by
  206. // them: [guardblock][allocation][guardblock]. numBytes is how
  207. // much memory the caller asked for. allocationSize is the total
  208. // size including guard blocks. In release build,
  209. // guardBlockSize=0 and this all gets optimized away.
  210. size_t allocationSize = TAllocation::allocationSize(numBytes);
  211. //
  212. // Just keep some interesting statistics.
  213. //
  214. ++numCalls;
  215. totalBytes += numBytes;
  216. //
  217. // Do the allocation, most likely case first, for efficiency.
  218. // This step could be moved to be inline sometime.
  219. //
  220. if (currentPageOffset + allocationSize <= pageSize) {
  221. //
  222. // Safe to allocate from currentPageOffset.
  223. //
  224. unsigned char* memory = reinterpret_cast<unsigned char*>(inUseList) + currentPageOffset;
  225. currentPageOffset += allocationSize;
  226. currentPageOffset = (currentPageOffset + alignmentMask) & ~alignmentMask;
  227. return initializeAllocation(inUseList, memory, numBytes);
  228. }
  229. if (allocationSize + headerSkip > pageSize) {
  230. //
  231. // Do a multi-page allocation. Don't mix these with the others.
  232. // The OS is efficient and allocating and free-ing multiple pages.
  233. //
  234. size_t numBytesToAlloc = allocationSize + headerSkip;
  235. tHeader* memory = reinterpret_cast<tHeader*>(::new char[numBytesToAlloc]);
  236. if (memory == 0)
  237. return 0;
  238. // Use placement-new to initialize header
  239. new(memory) tHeader(inUseList, (numBytesToAlloc + pageSize - 1) / pageSize);
  240. inUseList = memory;
  241. currentPageOffset = pageSize; // make next allocation come from a new page
  242. // No guard blocks for multi-page allocations (yet)
  243. return reinterpret_cast<void*>(reinterpret_cast<UINT_PTR>(memory) + headerSkip);
  244. }
  245. //
  246. // Need a simple page to allocate from.
  247. //
  248. tHeader* memory;
  249. if (freeList) {
  250. memory = freeList;
  251. freeList = freeList->nextPage;
  252. } else {
  253. memory = reinterpret_cast<tHeader*>(::new char[pageSize]);
  254. if (memory == 0)
  255. return 0;
  256. }
  257. // Use placement-new to initialize header
  258. new(memory) tHeader(inUseList, 1);
  259. inUseList = memory;
  260. unsigned char* ret = reinterpret_cast<unsigned char*>(inUseList) + headerSkip;
  261. currentPageOffset = (headerSkip + allocationSize + alignmentMask) & ~alignmentMask;
  262. return initializeAllocation(inUseList, ret, numBytes);
  263. }
  264. //
  265. // Check all allocations in a list for damage by calling check on each.
  266. //
  267. void TAllocation::checkAllocList() const
  268. {
  269. for (const TAllocation* alloc = this; alloc != 0; alloc = alloc->prevAlloc)
  270. alloc->check();
  271. }
  272. } // end namespace glslang