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