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 enum class ReleaseStrategy : uint8_t 60 { 61 OnlyMultiPage, 62 All, 63 }; 64 65 static const int kDefaultAlignment = sizeof(void *); 66 // 67 // Create PoolAllocator. If alignment is set to 1 byte then fastAllocate() 68 // function can be used to make allocations with less overhead. 69 // 70 PoolAllocator(int growthIncrement = 8 * 1024, int allocationAlignment = kDefaultAlignment); 71 72 // 73 // Don't call the destructor just to free up the memory, call pop() 74 // 75 ~PoolAllocator(); 76 77 // 78 // Initialize page size and alignment after construction 79 // 80 void initialize(int pageSize, int alignment); 81 82 // 83 // Call push() to establish a new place to pop memory to. Does not 84 // have to be called to get things started. 85 // 86 void push(); 87 88 // 89 // Call pop() to free all memory allocated since the last call to push(), 90 // or if no last call to push, frees all memory since first allocation. 91 // 92 void pop(ReleaseStrategy releaseStrategy = ReleaseStrategy::OnlyMultiPage); 93 94 // 95 // Call popAll() to free all memory allocated. 96 // 97 void popAll(); 98 99 // 100 // Call allocate() to actually acquire memory. Returns 0 if no memory 101 // available, otherwise a properly aligned pointer to 'numBytes' of memory. 102 // 103 void *allocate(size_t numBytes); 104 105 // 106 // Call fastAllocate() for a faster allocate function that does minimal bookkeeping 107 // preCondition: Allocator must have been created w/ alignment of 1 fastAllocate(size_t numBytes)108 ANGLE_INLINE uint8_t *fastAllocate(size_t numBytes) 109 { 110 #if defined(ANGLE_DISABLE_POOL_ALLOC) 111 return reinterpret_cast<uint8_t *>(allocate(numBytes)); 112 #else 113 ASSERT(mAlignment == 1); 114 // No multi-page allocations 115 ASSERT(numBytes <= (mPageSize - mPageHeaderSkip)); 116 // 117 // Do the allocation, most likely case inline first, for efficiency. 118 // 119 if (numBytes <= mPageSize - mCurrentPageOffset) 120 { 121 // 122 // Safe to allocate from mCurrentPageOffset. 123 // 124 uint8_t *memory = reinterpret_cast<uint8_t *>(mInUseList) + mCurrentPageOffset; 125 mCurrentPageOffset += numBytes; 126 return memory; 127 } 128 return allocateNewPage(numBytes); 129 #endif 130 } 131 132 // There is no deallocate. The point of this class is that deallocation can be skipped by the 133 // user of it, as the model of use is to simultaneously deallocate everything at once by calling 134 // pop(), and to not have to solve memory leak problems. 135 136 // Catch unwanted allocations. 137 // TODO(jmadill): Remove this when we remove the global allocator. 138 void lock(); 139 void unlock(); 140 141 private: 142 size_t mAlignment; // all returned allocations will be aligned at 143 // this granularity, which will be a power of 2 144 #if !defined(ANGLE_DISABLE_POOL_ALLOC) 145 struct AllocState 146 { 147 size_t offset; 148 PageHeader *page; 149 }; 150 using AllocStack = std::vector<AllocState>; 151 152 // Slow path of allocation when we have to get a new page. 153 uint8_t *allocateNewPage(size_t numBytes); 154 // Track allocations if and only if we're using guard blocks 155 void *initializeAllocation(uint8_t *memory, size_t numBytes); 156 157 // Granularity of allocation from the OS 158 size_t mPageSize; 159 // Amount of memory to skip to make room for the page header (which is the size of the page 160 // header, or PageHeader in PoolAlloc.cpp) 161 size_t mPageHeaderSkip; 162 // Next offset in top of inUseList to allocate from. This offset is not necessarily aligned to 163 // anything. When an allocation is made, the data is aligned to mAlignment, and the header (if 164 // any) will align to pointer size by extension (since mAlignment is made aligned to at least 165 // pointer size). 166 size_t mCurrentPageOffset; 167 // List of popped memory 168 PageHeader *mFreeList; 169 // List of all memory currently being used. The head of this list is where allocations are 170 // currently being made from. 171 PageHeader *mInUseList; 172 // Stack of where to allocate from, to partition pool 173 AllocStack mStack; 174 175 int mNumCalls; // just an interesting statistic 176 size_t mTotalBytes; // just an interesting statistic 177 178 #else // !defined(ANGLE_DISABLE_POOL_ALLOC) 179 std::vector<std::vector<void *>> mStack; 180 #endif 181 182 bool mLocked; 183 }; 184 185 } // namespace angle 186 187 #endif // COMMON_POOLALLOC_H_ 188