a0d8a7ef4b8082c491cc74cb2353218748626783
[platform/upstream/glslang.git] / glslang / MachineIndependent / PoolAlloc.cpp
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/PoolAlloc.h"
36 #include "../Include/Common.h"
37
38 #include "../Include/InitializeGlobals.h"
39 #include "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(0),
109     inUseList(0),
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 (int x = 0; x < guardBlockSize; x++) {
194         if (blockMem[x] != val) {
195                         const int maxSize = 80;
196             char assertMsg[80];
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
210 void TPoolAllocator::push()
211 {
212     tAllocState state = { currentPageOffset, inUseList };
213
214     stack.push_back(state);
215         
216     //
217     // Indicate there is no current page to allocate from.
218     //
219     currentPageOffset = pageSize;
220 }
221
222 //
223 // Do a mass-deallocation of all the individual allocations
224 // that have occurred since the last push(), or since the
225 // last pop(), or since the object's creation.
226 //
227 // The deallocated pages are saved for future allocations.
228 //
229 void TPoolAllocator::pop()
230 {
231     if (stack.size() < 1)
232         return;
233
234     tHeader* page = stack.back().page;
235     currentPageOffset = stack.back().offset;
236
237     while (inUseList != page) {
238         // invoke destructor to free allocation list
239         inUseList->~tHeader();
240         
241         tHeader* nextInUse = inUseList->nextPage;
242         if (inUseList->pageCount > 1)
243             delete [] reinterpret_cast<char*>(inUseList);
244         else {
245             inUseList->nextPage = freeList;
246             freeList = inUseList;
247         }
248         inUseList = nextInUse;
249     }
250
251     stack.pop_back();
252 }
253
254 //
255 // Do a mass-deallocation of all the individual allocations
256 // that have occurred.
257 //
258 void TPoolAllocator::popAll()
259 {
260     while (stack.size() > 0)
261         pop();
262 }
263
264 void* TPoolAllocator::allocate(size_t numBytes)
265 {
266     // If we are using guard blocks, all allocations are bracketed by
267     // them: [guardblock][allocation][guardblock].  numBytes is how
268     // much memory the caller asked for.  allocationSize is the total
269     // size including guard blocks.  In release build,
270     // guardBlockSize=0 and this all gets optimized away.
271     size_t allocationSize = TAllocation::allocationSize(numBytes);
272     
273     //
274     // Just keep some interesting statistics.
275     //
276     ++numCalls;
277     totalBytes += numBytes;
278
279     //
280     // Do the allocation, most likely case first, for efficiency.
281     // This step could be moved to be inline sometime.
282     //
283     if (currentPageOffset + allocationSize <= pageSize) {
284         //
285         // Safe to allocate from currentPageOffset.
286         //
287         unsigned char* memory = reinterpret_cast<unsigned char*>(inUseList) + currentPageOffset;
288         currentPageOffset += allocationSize;
289         currentPageOffset = (currentPageOffset + alignmentMask) & ~alignmentMask;
290
291         return initializeAllocation(inUseList, memory, numBytes);
292     }
293
294     if (allocationSize + headerSkip > pageSize) {
295         //
296         // Do a multi-page allocation.  Don't mix these with the others.
297         // The OS is efficient and allocating and free-ing multiple pages.
298         //
299         size_t numBytesToAlloc = allocationSize + headerSkip;
300         tHeader* memory = reinterpret_cast<tHeader*>(::new char[numBytesToAlloc]);
301         if (memory == 0)
302             return 0;
303
304         // Use placement-new to initialize header
305         new(memory) tHeader(inUseList, (numBytesToAlloc + pageSize - 1) / pageSize);
306         inUseList = memory;
307
308         currentPageOffset = pageSize;  // make next allocation come from a new page
309
310         // No guard blocks for multi-page allocations (yet)
311         return reinterpret_cast<void*>(reinterpret_cast<UINT_PTR>(memory) + headerSkip);
312     }
313
314     //
315     // Need a simple page to allocate from.
316     //
317     tHeader* memory;
318     if (freeList) {
319         memory = freeList;
320         freeList = freeList->nextPage;
321     } else {
322         memory = reinterpret_cast<tHeader*>(::new char[pageSize]);
323         if (memory == 0)
324             return 0;
325     }
326
327     // Use placement-new to initialize header
328     new(memory) tHeader(inUseList, 1);
329     inUseList = memory;
330     
331     unsigned char* ret = reinterpret_cast<unsigned char*>(inUseList) + headerSkip;
332     currentPageOffset = (headerSkip + allocationSize + alignmentMask) & ~alignmentMask;
333
334     return initializeAllocation(inUseList, ret, numBytes);
335 }
336
337
338 //
339 // Check all allocations in a list for damage by calling check on each.
340 //
341 void TAllocation::checkAllocList() const
342 {
343     for (const TAllocation* alloc = this; alloc != 0; alloc = alloc->prevAlloc)
344         alloc->check();
345 }
346
347 } // end namespace glslang