1 //
2 // Copyright 2019 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 // PoolAlloc.cpp:
7 //    Implements the class methods for PoolAllocator and Allocation classes.
8 //
9 
10 #include "common/PoolAlloc.h"
11 
12 #include <assert.h>
13 #include <stdint.h>
14 #include <stdio.h>
15 
16 #include "common/angleutils.h"
17 #include "common/debug.h"
18 #include "common/mathutil.h"
19 #include "common/platform.h"
20 #include "common/tls.h"
21 
22 namespace angle
23 {
24 
25 //
26 // Implement the functionality of the PoolAllocator class, which
27 // is documented in PoolAlloc.h.
28 //
PoolAllocator(int growthIncrement,int allocationAlignment)29 PoolAllocator::PoolAllocator(int growthIncrement, int allocationAlignment)
30     : mAlignment(allocationAlignment),
31 #if !defined(ANGLE_DISABLE_POOL_ALLOC)
32       mPageSize(growthIncrement),
33       mFreeList(0),
34       mInUseList(0),
35       mNumCalls(0),
36       mTotalBytes(0),
37 #endif
38       mLocked(false)
39 {
40 #if !defined(ANGLE_DISABLE_POOL_ALLOC)
41     if (mAlignment == 1)
42     {
43         // This is a special fast-path where fastAllocation() is enabled
44         mAlignmentMask = 0;
45         mHeaderSkip    = sizeof(Header);
46     }
47     else
48     {
49 #endif
50         //
51         // Adjust mAlignment to be at least pointer aligned and
52         // power of 2.
53         //
54         size_t minAlign = sizeof(void *);
55         mAlignment &= ~(minAlign - 1);
56         if (mAlignment < minAlign)
57             mAlignment = minAlign;
58         mAlignment     = gl::ceilPow2(static_cast<unsigned int>(mAlignment));
59         mAlignmentMask = mAlignment - 1;
60 
61 #if !defined(ANGLE_DISABLE_POOL_ALLOC)
62         //
63         // Align header skip
64         //
65         mHeaderSkip = minAlign;
66         if (mHeaderSkip < sizeof(Header))
67         {
68             mHeaderSkip = rx::roundUpPow2(sizeof(Header), mAlignment);
69         }
70     }
71     //
72     // Don't allow page sizes we know are smaller than all common
73     // OS page sizes.
74     //
75     if (mPageSize < 4 * 1024)
76         mPageSize = 4 * 1024;
77     //
78     // A large mCurrentPageOffset indicates a new page needs to
79     // be obtained to allocate memory.
80     //
81     mCurrentPageOffset = mPageSize;
82 #else  // !defined(ANGLE_DISABLE_POOL_ALLOC)
83     mStack.push_back({});
84 #endif
85 }
86 
~PoolAllocator()87 PoolAllocator::~PoolAllocator()
88 {
89 #if !defined(ANGLE_DISABLE_POOL_ALLOC)
90     while (mInUseList)
91     {
92         Header *next = mInUseList->nextPage;
93         mInUseList->~Header();
94         delete[] reinterpret_cast<char *>(mInUseList);
95         mInUseList = next;
96     }
97     // We should not check the guard blocks
98     // here, because we did it already when the block was
99     // placed into the free list.
100     //
101     while (mFreeList)
102     {
103         Header *next = mFreeList->nextPage;
104         delete[] reinterpret_cast<char *>(mFreeList);
105         mFreeList = next;
106     }
107 #else  // !defined(ANGLE_DISABLE_POOL_ALLOC)
108     for (auto &allocs : mStack)
109     {
110         for (auto alloc : allocs)
111         {
112             free(alloc);
113         }
114     }
115     mStack.clear();
116 #endif
117 }
118 
119 //
120 // Check a single guard block for damage
121 //
checkGuardBlock(unsigned char * blockMem,unsigned char val,const char * locText) const122 void Allocation::checkGuardBlock(unsigned char *blockMem,
123                                  unsigned char val,
124                                  const char *locText) const
125 {
126 #if defined(ANGLE_POOL_ALLOC_GUARD_BLOCKS)
127     for (size_t x = 0; x < kGuardBlockSize; x++)
128     {
129         if (blockMem[x] != val)
130         {
131             char assertMsg[80];
132             // We don't print the assert message.  It's here just to be helpful.
133             snprintf(assertMsg, sizeof(assertMsg),
134                      "PoolAlloc: Damage %s %zu byte allocation at 0x%p\n", locText, mSize, data());
135             assert(0 && "PoolAlloc: Damage in guard block");
136         }
137     }
138 #endif
139 }
140 
push()141 void PoolAllocator::push()
142 {
143 #if !defined(ANGLE_DISABLE_POOL_ALLOC)
144     AllocState state = {mCurrentPageOffset, mInUseList};
145 
146     mStack.push_back(state);
147 
148     //
149     // Indicate there is no current page to allocate from.
150     //
151     mCurrentPageOffset = mPageSize;
152 #else  // !defined(ANGLE_DISABLE_POOL_ALLOC)
153     mStack.push_back({});
154 #endif
155 }
156 
157 //
158 // Do a mass-deallocation of all the individual allocations
159 // that have occurred since the last push(), or since the
160 // last pop(), or since the object's creation.
161 //
162 // The deallocated pages are saved for future allocations.
163 //
pop()164 void PoolAllocator::pop()
165 {
166     if (mStack.size() < 1)
167         return;
168 
169 #if !defined(ANGLE_DISABLE_POOL_ALLOC)
170     Header *page       = mStack.back().page;
171     mCurrentPageOffset = mStack.back().offset;
172 
173     while (mInUseList != page)
174     {
175         // invoke destructor to free allocation list
176         mInUseList->~Header();
177 
178         Header *nextInUse = mInUseList->nextPage;
179         if (mInUseList->pageCount > 1)
180             delete[] reinterpret_cast<char *>(mInUseList);
181         else
182         {
183             mInUseList->nextPage = mFreeList;
184             mFreeList            = mInUseList;
185         }
186         mInUseList = nextInUse;
187     }
188 
189     mStack.pop_back();
190 #else  // !defined(ANGLE_DISABLE_POOL_ALLOC)
191     for (auto &alloc : mStack.back())
192     {
193         free(alloc);
194     }
195     mStack.pop_back();
196 #endif
197 }
198 
199 //
200 // Do a mass-deallocation of all the individual allocations
201 // that have occurred.
202 //
popAll()203 void PoolAllocator::popAll()
204 {
205     while (mStack.size() > 0)
206         pop();
207 }
208 
allocate(size_t numBytes)209 void *PoolAllocator::allocate(size_t numBytes)
210 {
211     ASSERT(!mLocked);
212 
213 #if !defined(ANGLE_DISABLE_POOL_ALLOC)
214     //
215     // Just keep some interesting statistics.
216     //
217     ++mNumCalls;
218     mTotalBytes += numBytes;
219 
220     // If we are using guard blocks, all allocations are bracketed by
221     // them: [guardblock][allocation][guardblock].  numBytes is how
222     // much memory the caller asked for.  allocationSize is the total
223     // size including guard blocks.  In release build,
224     // kGuardBlockSize=0 and this all gets optimized away.
225     size_t allocationSize = Allocation::AllocationSize(numBytes) + mAlignment;
226     // Detect integer overflow.
227     if (allocationSize < numBytes)
228         return 0;
229 
230     //
231     // Do the allocation, most likely case first, for efficiency.
232     // This step could be moved to be inline sometime.
233     //
234     if (allocationSize <= mPageSize - mCurrentPageOffset)
235     {
236         //
237         // Safe to allocate from mCurrentPageOffset.
238         //
239         unsigned char *memory = reinterpret_cast<unsigned char *>(mInUseList) + mCurrentPageOffset;
240         mCurrentPageOffset += allocationSize;
241         mCurrentPageOffset = (mCurrentPageOffset + mAlignmentMask) & ~mAlignmentMask;
242 
243         return initializeAllocation(mInUseList, memory, numBytes);
244     }
245 
246     if (allocationSize > mPageSize - mHeaderSkip)
247     {
248         //
249         // Do a multi-page allocation.  Don't mix these with the others.
250         // The OS is efficient in allocating and freeing multiple pages.
251         //
252         size_t numBytesToAlloc = allocationSize + mHeaderSkip;
253         // Detect integer overflow.
254         if (numBytesToAlloc < allocationSize)
255             return 0;
256 
257         Header *memory = reinterpret_cast<Header *>(::new char[numBytesToAlloc]);
258         if (memory == 0)
259             return 0;
260 
261         // Use placement-new to initialize header
262         new (memory) Header(mInUseList, (numBytesToAlloc + mPageSize - 1) / mPageSize);
263         mInUseList = memory;
264 
265         mCurrentPageOffset = mPageSize;  // make next allocation come from a new page
266 
267         // No guard blocks for multi-page allocations (yet)
268         void *unalignedPtr =
269             reinterpret_cast<void *>(reinterpret_cast<uintptr_t>(memory) + mHeaderSkip);
270         return std::align(mAlignment, numBytes, unalignedPtr, allocationSize);
271     }
272     unsigned char *newPageAddr =
273         static_cast<unsigned char *>(allocateNewPage(numBytes, allocationSize));
274     return initializeAllocation(mInUseList, newPageAddr, numBytes);
275 #else  // !defined(ANGLE_DISABLE_POOL_ALLOC)
276     void *alloc = malloc(numBytes + mAlignmentMask);
277     mStack.back().push_back(alloc);
278 
279     intptr_t intAlloc = reinterpret_cast<intptr_t>(alloc);
280     intAlloc          = (intAlloc + mAlignmentMask) & ~mAlignmentMask;
281     return reinterpret_cast<void *>(intAlloc);
282 #endif
283 }
284 
285 #if !defined(ANGLE_DISABLE_POOL_ALLOC)
allocateNewPage(size_t numBytes,size_t allocationSize)286 void *PoolAllocator::allocateNewPage(size_t numBytes, size_t allocationSize)
287 {
288     //
289     // Need a simple page to allocate from.
290     //
291     Header *memory;
292     if (mFreeList)
293     {
294         memory    = mFreeList;
295         mFreeList = mFreeList->nextPage;
296     }
297     else
298     {
299         memory = reinterpret_cast<Header *>(::new char[mPageSize]);
300         if (memory == 0)
301             return 0;
302     }
303     // Use placement-new to initialize header
304     new (memory) Header(mInUseList, 1);
305     mInUseList = memory;
306 
307     unsigned char *ret = reinterpret_cast<unsigned char *>(mInUseList) + mHeaderSkip;
308     mCurrentPageOffset = (mHeaderSkip + allocationSize + mAlignmentMask) & ~mAlignmentMask;
309     return ret;
310 }
311 #endif
312 
lock()313 void PoolAllocator::lock()
314 {
315     ASSERT(!mLocked);
316     mLocked = true;
317 }
318 
unlock()319 void PoolAllocator::unlock()
320 {
321     ASSERT(mLocked);
322     mLocked = false;
323 }
324 
325 //
326 // Check all allocations in a list for damage by calling check on each.
327 //
checkAllocList() const328 void Allocation::checkAllocList() const
329 {
330     for (const Allocation *alloc = this; alloc != 0; alloc = alloc->mPrevAlloc)
331         alloc->check();
332 }
333 
334 }  // namespace angle
335