1 /* 2 * Copyright 2012 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8 #include "GrMemoryPool.h" 9 #include "SkMalloc.h" 10 #ifdef SK_DEBUG 11 #include "SkAtomics.h" 12 #endif 13 14 #ifdef SK_DEBUG 15 #define VALIDATE this->validate() 16 #else 17 #define VALIDATE 18 #endif 19 20 constexpr size_t GrMemoryPool::kSmallestMinAllocSize; 21 22 GrMemoryPool::GrMemoryPool(size_t preallocSize, size_t minAllocSize) { 23 SkDEBUGCODE(fAllocationCnt = 0); 24 SkDEBUGCODE(fAllocBlockCnt = 0); 25 26 minAllocSize = SkTMax<size_t>(GrSizeAlignUp(minAllocSize, kAlignment), kSmallestMinAllocSize); 27 preallocSize = SkTMax<size_t>(GrSizeAlignUp(preallocSize, kAlignment), minAllocSize); 28 29 fMinAllocSize = minAllocSize; 30 fSize = 0; 31 32 fHead = CreateBlock(preallocSize); 33 fTail = fHead; 34 fHead->fNext = nullptr; 35 fHead->fPrev = nullptr; 36 VALIDATE; 37 }; 38 39 GrMemoryPool::~GrMemoryPool() { 40 VALIDATE; 41 #ifdef SK_DEBUG 42 int i = 0; 43 int n = fAllocatedIDs.count(); 44 fAllocatedIDs.foreach([&i, n] (int32_t id) { 45 if (++i == 1) { 46 SkDebugf("Leaked IDs (in no particular order): %d", id); 47 } else if (i < 11) { 48 SkDebugf(", %d%s", id, (n == i ? "\n" : "")); 49 } else if (i == 11) { 50 SkDebugf(", ...\n"); 51 } 52 }); 53 #endif 54 SkASSERT(0 == fAllocationCnt); 55 SkASSERT(fHead == fTail); 56 SkASSERT(0 == fHead->fLiveCount); 57 DeleteBlock(fHead); 58 }; 59 60 void* GrMemoryPool::allocate(size_t size) { 61 VALIDATE; 62 size += kPerAllocPad; 63 size = GrSizeAlignUp(size, kAlignment); 64 if (fTail->fFreeSize < size) { 65 size_t blockSize = size + kHeaderSize; 66 blockSize = SkTMax<size_t>(blockSize, fMinAllocSize); 67 BlockHeader* block = CreateBlock(blockSize); 68 69 block->fPrev = fTail; 70 block->fNext = nullptr; 71 SkASSERT(nullptr == fTail->fNext); 72 fTail->fNext = block; 73 fTail = block; 74 fSize += block->fSize; 75 SkDEBUGCODE(++fAllocBlockCnt); 76 } 77 SkASSERT(kAssignedMarker == fTail->fBlockSentinal); 78 SkASSERT(fTail->fFreeSize >= size); 79 intptr_t ptr = fTail->fCurrPtr; 80 // We stash a pointer to the block header, just before the allocated space, 81 // so that we can decrement the live count on delete in constant time. 82 AllocHeader* allocData = reinterpret_cast<AllocHeader*>(ptr); 83 SkDEBUGCODE(allocData->fSentinal = kAssignedMarker); 84 SkDEBUGCODE(allocData->fID = []{static int32_t gID; return sk_atomic_inc(&gID) + 1;}()); 85 // You can set a breakpoint here when a leaked ID is allocated to see the stack frame. 86 SkDEBUGCODE(fAllocatedIDs.add(allocData->fID)); 87 allocData->fHeader = fTail; 88 ptr += kPerAllocPad; 89 fTail->fPrevPtr = fTail->fCurrPtr; 90 fTail->fCurrPtr += size; 91 fTail->fFreeSize -= size; 92 fTail->fLiveCount += 1; 93 SkDEBUGCODE(++fAllocationCnt); 94 VALIDATE; 95 return reinterpret_cast<void*>(ptr); 96 } 97 98 void GrMemoryPool::release(void* p) { 99 VALIDATE; 100 intptr_t ptr = reinterpret_cast<intptr_t>(p) - kPerAllocPad; 101 AllocHeader* allocData = reinterpret_cast<AllocHeader*>(ptr); 102 SkASSERT(kAssignedMarker == allocData->fSentinal); 103 SkDEBUGCODE(allocData->fSentinal = kFreedMarker); 104 SkDEBUGCODE(fAllocatedIDs.remove(allocData->fID)); 105 BlockHeader* block = allocData->fHeader; 106 SkASSERT(kAssignedMarker == block->fBlockSentinal); 107 if (1 == block->fLiveCount) { 108 // the head block is special, it is reset rather than deleted 109 if (fHead == block) { 110 fHead->fCurrPtr = reinterpret_cast<intptr_t>(fHead) + kHeaderSize; 111 fHead->fLiveCount = 0; 112 fHead->fFreeSize = fHead->fSize - kHeaderSize; 113 } else { 114 BlockHeader* prev = block->fPrev; 115 BlockHeader* next = block->fNext; 116 SkASSERT(prev); 117 prev->fNext = next; 118 if (next) { 119 next->fPrev = prev; 120 } else { 121 SkASSERT(fTail == block); 122 fTail = prev; 123 } 124 fSize -= block->fSize; 125 DeleteBlock(block); 126 SkDEBUGCODE(fAllocBlockCnt--); 127 } 128 } else { 129 --block->fLiveCount; 130 // Trivial reclaim: if we're releasing the most recent allocation, reuse it 131 if (block->fPrevPtr == ptr) { 132 block->fFreeSize += (block->fCurrPtr - block->fPrevPtr); 133 block->fCurrPtr = block->fPrevPtr; 134 } 135 } 136 SkDEBUGCODE(--fAllocationCnt); 137 VALIDATE; 138 } 139 140 GrMemoryPool::BlockHeader* GrMemoryPool::CreateBlock(size_t blockSize) { 141 blockSize = SkTMax<size_t>(blockSize, kHeaderSize); 142 BlockHeader* block = 143 reinterpret_cast<BlockHeader*>(sk_malloc_throw(blockSize)); 144 // we assume malloc gives us aligned memory 145 SkASSERT(!(reinterpret_cast<intptr_t>(block) % kAlignment)); 146 SkDEBUGCODE(block->fBlockSentinal = kAssignedMarker); 147 block->fLiveCount = 0; 148 block->fFreeSize = blockSize - kHeaderSize; 149 block->fCurrPtr = reinterpret_cast<intptr_t>(block) + kHeaderSize; 150 block->fPrevPtr = 0; // gcc warns on assigning nullptr to an intptr_t. 151 block->fSize = blockSize; 152 return block; 153 } 154 155 void GrMemoryPool::DeleteBlock(BlockHeader* block) { 156 SkASSERT(kAssignedMarker == block->fBlockSentinal); 157 SkDEBUGCODE(block->fBlockSentinal = kFreedMarker); // FWIW 158 sk_free(block); 159 } 160 161 void GrMemoryPool::validate() { 162 #ifdef SK_DEBUG 163 BlockHeader* block = fHead; 164 BlockHeader* prev = nullptr; 165 SkASSERT(block); 166 int allocCount = 0; 167 do { 168 SkASSERT(kAssignedMarker == block->fBlockSentinal); 169 allocCount += block->fLiveCount; 170 SkASSERT(prev == block->fPrev); 171 if (prev) { 172 SkASSERT(prev->fNext == block); 173 } 174 175 intptr_t b = reinterpret_cast<intptr_t>(block); 176 size_t ptrOffset = block->fCurrPtr - b; 177 size_t totalSize = ptrOffset + block->fFreeSize; 178 intptr_t userStart = b + kHeaderSize; 179 180 SkASSERT(!(b % kAlignment)); 181 SkASSERT(!(totalSize % kAlignment)); 182 SkASSERT(!(block->fCurrPtr % kAlignment)); 183 if (fHead != block) { 184 SkASSERT(block->fLiveCount); 185 SkASSERT(totalSize >= fMinAllocSize); 186 } else { 187 SkASSERT(totalSize == block->fSize); 188 } 189 if (!block->fLiveCount) { 190 SkASSERT(ptrOffset == kHeaderSize); 191 SkASSERT(userStart == block->fCurrPtr); 192 } else { 193 AllocHeader* allocData = reinterpret_cast<AllocHeader*>(userStart); 194 SkASSERT(allocData->fSentinal == kAssignedMarker || 195 allocData->fSentinal == kFreedMarker); 196 SkASSERT(block == allocData->fHeader); 197 } 198 199 prev = block; 200 } while ((block = block->fNext)); 201 SkASSERT(allocCount == fAllocationCnt); 202 SkASSERT(fAllocationCnt == fAllocatedIDs.count()); 203 SkASSERT(prev == fTail); 204 SkASSERT(fAllocBlockCnt != 0 || fSize == 0); 205 #endif 206 } 207