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