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.h: 7 // Defines the class interface for PoolAllocator. 8 // 9 10 #ifndef COMMON_POOLALLOC_H_ 11 #define COMMON_POOLALLOC_H_ 12 13 #if !defined(NDEBUG) 14 # define ANGLE_POOL_ALLOC_GUARD_BLOCKS // define to enable guard block checking 15 #endif 16 17 // 18 // This header defines an allocator that can be used to efficiently 19 // allocate a large number of small requests for heap memory, with the 20 // intention that they are not individually deallocated, but rather 21 // collectively deallocated at one time. 22 // 23 // This simultaneously 24 // 25 // * Makes each individual allocation much more efficient; the 26 // typical allocation is trivial. 27 // * Completely avoids the cost of doing individual deallocation. 28 // * Saves the trouble of tracking down and plugging a large class of leaks. 29 // 30 // Individual classes can use this allocator by supplying their own 31 // new and delete methods. 32 // 33 34 #include "angleutils.h" 35 #include "common/debug.h" 36 37 namespace angle 38 { 39 class Allocation; 40 class PageHeader; 41 42 // 43 // There are several stacks. One is to track the pushing and popping 44 // of the user, and not yet implemented. The others are simply a 45 // repositories of free pages or used pages. 46 // 47 // Page stacks are linked together with a simple header at the beginning 48 // of each allocation obtained from the underlying OS. Multi-page allocations 49 // are returned to the OS. Individual page allocations are kept for future 50 // re-use. 51 // 52 // The "page size" used is not, nor must it match, the underlying OS 53 // page size. But, having it be about that size or equal to a set of 54 // pages is likely most optimal. 55 // 56 class PoolAllocator : angle::NonCopyable 57 { 58 public: 59 static const int kDefaultAlignment = sizeof(void *); 60 // 61 // Create PoolAllocator. If alignment is set to 1 byte then fastAllocate() 62 // function can be used to make allocations with less overhead. 63 // 64 PoolAllocator(int growthIncrement = 8 * 1024, int allocationAlignment = kDefaultAlignment); 65 66 // 67 // Don't call the destructor just to free up the memory, call pop() 68 // 69 ~PoolAllocator(); 70 71 // 72 // Initialize page size and alignment after construction 73 // 74 void initialize(int pageSize, int alignment); 75 76 // 77 // Call push() to establish a new place to pop memory to. Does not 78 // have to be called to get things started. 79 // 80 void push(); 81 82 // 83 // Call pop() to free all memory allocated since the last call to push(), 84 // or if no last call to push, frees all memory since first allocation. 85 // 86 void pop(); 87 88 // 89 // Call popAll() to free all memory allocated. 90 // 91 void popAll(); 92 93 // 94 // Call allocate() to actually acquire memory. Returns 0 if no memory 95 // available, otherwise a properly aligned pointer to 'numBytes' of memory. 96 // 97 void *allocate(size_t numBytes); 98 99 // 100 // Call fastAllocate() for a faster allocate function that does minimal bookkeeping 101 // preCondition: Allocator must have been created w/ alignment of 1 fastAllocate(size_t numBytes)102 ANGLE_INLINE uint8_t *fastAllocate(size_t numBytes) 103 { 104 #if defined(ANGLE_DISABLE_POOL_ALLOC) 105 return reinterpret_cast<uint8_t *>(allocate(numBytes)); 106 #else 107 ASSERT(mAlignment == 1); 108 // No multi-page allocations 109 ASSERT(numBytes <= (mPageSize - mPageHeaderSkip)); 110 // 111 // Do the allocation, most likely case inline first, for efficiency. 112 // 113 if (numBytes <= mPageSize - mCurrentPageOffset) 114 { 115 // 116 // Safe to allocate from mCurrentPageOffset. 117 // 118 uint8_t *memory = reinterpret_cast<uint8_t *>(mInUseList) + mCurrentPageOffset; 119 mCurrentPageOffset += numBytes; 120 return memory; 121 } 122 return allocateNewPage(numBytes); 123 #endif 124 } 125 126 // There is no deallocate. The point of this class is that deallocation can be skipped by the 127 // user of it, as the model of use is to simultaneously deallocate everything at once by calling 128 // pop(), and to not have to solve memory leak problems. 129 130 // Catch unwanted allocations. 131 // TODO(jmadill): Remove this when we remove the global allocator. 132 void lock(); 133 void unlock(); 134 135 private: 136 size_t mAlignment; // all returned allocations will be aligned at 137 // this granularity, which will be a power of 2 138 #if !defined(ANGLE_DISABLE_POOL_ALLOC) 139 struct AllocState 140 { 141 size_t offset; 142 PageHeader *page; 143 }; 144 using AllocStack = std::vector<AllocState>; 145 146 // Slow path of allocation when we have to get a new page. 147 uint8_t *allocateNewPage(size_t numBytes); 148 // Track allocations if and only if we're using guard blocks 149 void *initializeAllocation(uint8_t *memory, size_t numBytes); 150 151 // Granularity of allocation from the OS 152 size_t mPageSize; 153 // Amount of memory to skip to make room for the page header (which is the size of the page 154 // header, or PageHeader in PoolAlloc.cpp) 155 size_t mPageHeaderSkip; 156 // Next offset in top of inUseList to allocate from. This offset is not necessarily aligned to 157 // anything. When an allocation is made, the data is aligned to mAlignment, and the header (if 158 // any) will align to pointer size by extension (since mAlignment is made aligned to at least 159 // pointer size). 160 size_t mCurrentPageOffset; 161 // List of popped memory 162 PageHeader *mFreeList; 163 // List of all memory currently being used. The head of this list is where allocations are 164 // currently being made from. 165 PageHeader *mInUseList; 166 // Stack of where to allocate from, to partition pool 167 AllocStack mStack; 168 169 int mNumCalls; // just an interesting statistic 170 size_t mTotalBytes; // just an interesting statistic 171 172 #else // !defined(ANGLE_DISABLE_POOL_ALLOC) 173 std::vector<std::vector<void *>> mStack; 174 #endif 175 176 bool mLocked; 177 }; 178 179 } // namespace angle 180 181 #endif // COMMON_POOLALLOC_H_ 182