blob: 013e75258cbc19caf7d2829e6e1476d8ec0e867d [file] [log] [blame]
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001//
2// Copyright (c) 2002-2010 The ANGLE Project Authors. All rights reserved.
3// Use of this source code is governed by a BSD-style license that can be
4// found in the LICENSE file.
5//
6
7#include "PoolAlloc.h"
8#include "Common.h"
9
10#include "InitializeGlobals.h"
11#include "osinclude.h"
12
13OS_TLSIndex PoolIndex = TLS_OUT_OF_INDEXES;
14
15void InitializeGlobalPools()
16{
17 TThreadGlobalPools* globalPools= static_cast<TThreadGlobalPools*>(OS_GetTLSValue(PoolIndex));
18 if (globalPools)
19 return;
20
21 TPoolAllocator *globalPoolAllocator = new TPoolAllocator(true);
22
23 TThreadGlobalPools* threadData = new TThreadGlobalPools();
24
25 threadData->globalPoolAllocator = globalPoolAllocator;
26
27 OS_SetTLSValue(PoolIndex, threadData);
28 globalPoolAllocator->push();
29}
30
31void FreeGlobalPools()
32{
33 // Release the allocated memory for this thread.
34 TThreadGlobalPools* globalPools= static_cast<TThreadGlobalPools*>(OS_GetTLSValue(PoolIndex));
35 if (!globalPools)
36 return;
37
38 GlobalPoolAllocator.popAll();
39 delete &GlobalPoolAllocator;
40 delete globalPools;
41}
42
43bool InitializePoolIndex()
44{
45 // Allocate a TLS index.
46 if ((PoolIndex = OS_AllocTLSIndex()) == OS_INVALID_TLS_INDEX)
47 return false;
48
49 return true;
50}
51
52void FreePoolIndex()
53{
54 // Release the TLS index.
55 OS_FreeTLSIndex(PoolIndex);
56}
57
58TPoolAllocator& GetGlobalPoolAllocator()
59{
60 TThreadGlobalPools* threadData = static_cast<TThreadGlobalPools*>(OS_GetTLSValue(PoolIndex));
61
62 return *threadData->globalPoolAllocator;
63}
64
65void SetGlobalPoolAllocatorPtr(TPoolAllocator* poolAllocator)
66{
67 TThreadGlobalPools* threadData = static_cast<TThreadGlobalPools*>(OS_GetTLSValue(PoolIndex));
68
69 threadData->globalPoolAllocator = poolAllocator;
70}
71
72//
73// Implement the functionality of the TPoolAllocator class, which
74// is documented in PoolAlloc.h.
75//
76TPoolAllocator::TPoolAllocator(bool g, int growthIncrement, int allocationAlignment) :
77 global(g),
78 pageSize(growthIncrement),
79 alignment(allocationAlignment),
80 freeList(0),
81 inUseList(0),
82 numCalls(0)
83{
84 //
85 // Don't allow page sizes we know are smaller than all common
86 // OS page sizes.
87 //
88 if (pageSize < 4*1024)
89 pageSize = 4*1024;
90
91 //
92 // A large currentPageOffset indicates a new page needs to
93 // be obtained to allocate memory.
94 //
95 currentPageOffset = pageSize;
96
97 //
98 // Adjust alignment to be at least pointer aligned and
99 // power of 2.
100 //
101 size_t minAlign = sizeof(void*);
102 alignment &= ~(minAlign - 1);
103 if (alignment < minAlign)
104 alignment = minAlign;
105 size_t a = 1;
106 while (a < alignment)
107 a <<= 1;
108 alignment = a;
109 alignmentMask = a - 1;
110
111 //
112 // Align header skip
113 //
114 headerSkip = minAlign;
115 if (headerSkip < sizeof(tHeader)) {
116 headerSkip = (sizeof(tHeader) + alignmentMask) & ~alignmentMask;
117 }
118}
119
120TPoolAllocator::~TPoolAllocator()
121{
122 if (!global) {
123 //
124 // Then we know that this object is not being
125 // allocated after other, globally scoped objects
126 // that depend on it. So we can delete the "in use" memory.
127 //
128 while (inUseList) {
129 tHeader* next = inUseList->nextPage;
130 inUseList->~tHeader();
131 delete [] reinterpret_cast<char*>(inUseList);
132 inUseList = next;
133 }
134 }
135
136 //
137 // Always delete the free list memory - it can't be being
138 // (correctly) referenced, whether the pool allocator was
139 // global or not. We should not check the guard blocks
140 // here, because we did it already when the block was
141 // placed into the free list.
142 //
143 while (freeList) {
144 tHeader* next = freeList->nextPage;
145 delete [] reinterpret_cast<char*>(freeList);
146 freeList = next;
147 }
148}
149
150// Support MSVC++ 6.0
151const unsigned char TAllocation::guardBlockBeginVal = 0xfb;
152const unsigned char TAllocation::guardBlockEndVal = 0xfe;
153const unsigned char TAllocation::userDataFill = 0xcd;
154
155# ifdef GUARD_BLOCKS
156 const size_t TAllocation::guardBlockSize = 16;
157# else
158 const size_t TAllocation::guardBlockSize = 0;
159# endif
160
161//
162// Check a single guard block for damage
163//
164void TAllocation::checkGuardBlock(unsigned char* blockMem, unsigned char val, const char* locText) const
165{
166 for (size_t x = 0; x < guardBlockSize; x++) {
167 if (blockMem[x] != val) {
168 char assertMsg[80];
169
170 // We don't print the assert message. It's here just to be helpful.
171 sprintf(assertMsg, "PoolAlloc: Damage %s %lu byte allocation at 0x%p\n",
172 locText, size, data());
173 assert(0 && "PoolAlloc: Damage in guard block");
174 }
175 }
176}
177
178
179void TPoolAllocator::push()
180{
181 tAllocState state = { currentPageOffset, inUseList };
182
183 stack.push_back(state);
184
185 //
186 // Indicate there is no current page to allocate from.
187 //
188 currentPageOffset = pageSize;
189}
190
191//
192// Do a mass-deallocation of all the individual allocations
193// that have occurred since the last push(), or since the
194// last pop(), or since the object's creation.
195//
196// The deallocated pages are saved for future allocations.
197//
198void TPoolAllocator::pop()
199{
200 if (stack.size() < 1)
201 return;
202
203 tHeader* page = stack.back().page;
204 currentPageOffset = stack.back().offset;
205
206 while (inUseList != page) {
207 // invoke destructor to free allocation list
208 inUseList->~tHeader();
209
210 tHeader* nextInUse = inUseList->nextPage;
211 if (inUseList->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//
227void TPoolAllocator::popAll()
228{
229 while (stack.size() > 0)
230 pop();
231}
232
233void* 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 == 0)
271 return 0;
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 == 0)
293 return 0;
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//
308// Check all allocations in a list for damage by calling check on each.
309//
310void TAllocation::checkAllocList() const
311{
312 for (const TAllocation* alloc = this; alloc != 0; alloc = alloc->prevAlloc)
313 alloc->check();
314}