Line data Source code
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 0 : GrMemoryPool::GrMemoryPool(size_t preallocSize, size_t minAllocSize) {
23 0 : SkDEBUGCODE(fAllocationCnt = 0);
24 0 : SkDEBUGCODE(fAllocBlockCnt = 0);
25 :
26 0 : minAllocSize = SkTMax<size_t>(GrSizeAlignUp(minAllocSize, kAlignment), kSmallestMinAllocSize);
27 0 : preallocSize = SkTMax<size_t>(GrSizeAlignUp(preallocSize, kAlignment), minAllocSize);
28 :
29 0 : fMinAllocSize = minAllocSize;
30 0 : fSize = 0;
31 :
32 0 : fHead = CreateBlock(preallocSize);
33 0 : fTail = fHead;
34 0 : fHead->fNext = nullptr;
35 0 : fHead->fPrev = nullptr;
36 0 : VALIDATE;
37 0 : };
38 :
39 0 : GrMemoryPool::~GrMemoryPool() {
40 0 : VALIDATE;
41 : #ifdef SK_DEBUG
42 0 : int i = 0;
43 0 : int n = fAllocatedIDs.count();
44 0 : fAllocatedIDs.foreach([&i, n] (int32_t id) {
45 0 : if (++i == 1) {
46 0 : SkDebugf("Leaked IDs (in no particular order): %d", id);
47 0 : } else if (i < 11) {
48 0 : SkDebugf(", %d%s", id, (n == i ? "\n" : ""));
49 0 : } else if (i == 11) {
50 0 : SkDebugf(", ...\n");
51 : }
52 0 : });
53 : #endif
54 0 : SkASSERT(0 == fAllocationCnt);
55 0 : SkASSERT(fHead == fTail);
56 0 : SkASSERT(0 == fHead->fLiveCount);
57 0 : DeleteBlock(fHead);
58 0 : };
59 :
60 0 : void* GrMemoryPool::allocate(size_t size) {
61 0 : VALIDATE;
62 0 : size += kPerAllocPad;
63 0 : size = GrSizeAlignUp(size, kAlignment);
64 0 : if (fTail->fFreeSize < size) {
65 0 : size_t blockSize = size + kHeaderSize;
66 0 : blockSize = SkTMax<size_t>(blockSize, fMinAllocSize);
67 0 : BlockHeader* block = CreateBlock(blockSize);
68 :
69 0 : block->fPrev = fTail;
70 0 : block->fNext = nullptr;
71 0 : SkASSERT(nullptr == fTail->fNext);
72 0 : fTail->fNext = block;
73 0 : fTail = block;
74 0 : fSize += block->fSize;
75 0 : SkDEBUGCODE(++fAllocBlockCnt);
76 : }
77 0 : SkASSERT(kAssignedMarker == fTail->fBlockSentinal);
78 0 : SkASSERT(fTail->fFreeSize >= size);
79 0 : 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 0 : AllocHeader* allocData = reinterpret_cast<AllocHeader*>(ptr);
83 0 : SkDEBUGCODE(allocData->fSentinal = kAssignedMarker);
84 0 : 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 0 : SkDEBUGCODE(fAllocatedIDs.add(allocData->fID));
87 0 : allocData->fHeader = fTail;
88 0 : ptr += kPerAllocPad;
89 0 : fTail->fPrevPtr = fTail->fCurrPtr;
90 0 : fTail->fCurrPtr += size;
91 0 : fTail->fFreeSize -= size;
92 0 : fTail->fLiveCount += 1;
93 0 : SkDEBUGCODE(++fAllocationCnt);
94 0 : VALIDATE;
95 0 : return reinterpret_cast<void*>(ptr);
96 : }
97 :
98 0 : void GrMemoryPool::release(void* p) {
99 0 : VALIDATE;
100 0 : intptr_t ptr = reinterpret_cast<intptr_t>(p) - kPerAllocPad;
101 0 : AllocHeader* allocData = reinterpret_cast<AllocHeader*>(ptr);
102 0 : SkASSERT(kAssignedMarker == allocData->fSentinal);
103 0 : SkDEBUGCODE(allocData->fSentinal = kFreedMarker);
104 0 : SkDEBUGCODE(fAllocatedIDs.remove(allocData->fID));
105 0 : BlockHeader* block = allocData->fHeader;
106 0 : SkASSERT(kAssignedMarker == block->fBlockSentinal);
107 0 : if (1 == block->fLiveCount) {
108 : // the head block is special, it is reset rather than deleted
109 0 : if (fHead == block) {
110 0 : fHead->fCurrPtr = reinterpret_cast<intptr_t>(fHead) + kHeaderSize;
111 0 : fHead->fLiveCount = 0;
112 0 : fHead->fFreeSize = fHead->fSize - kHeaderSize;
113 : } else {
114 0 : BlockHeader* prev = block->fPrev;
115 0 : BlockHeader* next = block->fNext;
116 0 : SkASSERT(prev);
117 0 : prev->fNext = next;
118 0 : if (next) {
119 0 : next->fPrev = prev;
120 : } else {
121 0 : SkASSERT(fTail == block);
122 0 : fTail = prev;
123 : }
124 0 : fSize -= block->fSize;
125 0 : DeleteBlock(block);
126 0 : SkDEBUGCODE(fAllocBlockCnt--);
127 : }
128 : } else {
129 0 : --block->fLiveCount;
130 : // Trivial reclaim: if we're releasing the most recent allocation, reuse it
131 0 : if (block->fPrevPtr == ptr) {
132 0 : block->fFreeSize += (block->fCurrPtr - block->fPrevPtr);
133 0 : block->fCurrPtr = block->fPrevPtr;
134 : }
135 : }
136 0 : SkDEBUGCODE(--fAllocationCnt);
137 0 : VALIDATE;
138 0 : }
139 :
140 0 : GrMemoryPool::BlockHeader* GrMemoryPool::CreateBlock(size_t blockSize) {
141 0 : blockSize = SkTMax<size_t>(blockSize, kHeaderSize);
142 : BlockHeader* block =
143 0 : reinterpret_cast<BlockHeader*>(sk_malloc_throw(blockSize));
144 : // we assume malloc gives us aligned memory
145 0 : SkASSERT(!(reinterpret_cast<intptr_t>(block) % kAlignment));
146 0 : SkDEBUGCODE(block->fBlockSentinal = kAssignedMarker);
147 0 : block->fLiveCount = 0;
148 0 : block->fFreeSize = blockSize - kHeaderSize;
149 0 : block->fCurrPtr = reinterpret_cast<intptr_t>(block) + kHeaderSize;
150 0 : block->fPrevPtr = 0; // gcc warns on assigning nullptr to an intptr_t.
151 0 : block->fSize = blockSize;
152 0 : return block;
153 : }
154 :
155 0 : void GrMemoryPool::DeleteBlock(BlockHeader* block) {
156 0 : SkASSERT(kAssignedMarker == block->fBlockSentinal);
157 0 : SkDEBUGCODE(block->fBlockSentinal = kFreedMarker); // FWIW
158 0 : sk_free(block);
159 0 : }
160 :
161 0 : void GrMemoryPool::validate() {
162 : #ifdef SK_DEBUG
163 0 : BlockHeader* block = fHead;
164 0 : BlockHeader* prev = nullptr;
165 0 : SkASSERT(block);
166 0 : int allocCount = 0;
167 0 : do {
168 0 : SkASSERT(kAssignedMarker == block->fBlockSentinal);
169 0 : allocCount += block->fLiveCount;
170 0 : SkASSERT(prev == block->fPrev);
171 0 : if (prev) {
172 0 : SkASSERT(prev->fNext == block);
173 : }
174 :
175 0 : intptr_t b = reinterpret_cast<intptr_t>(block);
176 0 : size_t ptrOffset = block->fCurrPtr - b;
177 0 : size_t totalSize = ptrOffset + block->fFreeSize;
178 0 : intptr_t userStart = b + kHeaderSize;
179 :
180 0 : SkASSERT(!(b % kAlignment));
181 0 : SkASSERT(!(totalSize % kAlignment));
182 0 : SkASSERT(!(block->fCurrPtr % kAlignment));
183 0 : if (fHead != block) {
184 0 : SkASSERT(block->fLiveCount);
185 0 : SkASSERT(totalSize >= fMinAllocSize);
186 : } else {
187 0 : SkASSERT(totalSize == block->fSize);
188 : }
189 0 : if (!block->fLiveCount) {
190 0 : SkASSERT(ptrOffset == kHeaderSize);
191 0 : SkASSERT(userStart == block->fCurrPtr);
192 : } else {
193 0 : AllocHeader* allocData = reinterpret_cast<AllocHeader*>(userStart);
194 0 : SkASSERT(allocData->fSentinal == kAssignedMarker ||
195 : allocData->fSentinal == kFreedMarker);
196 0 : SkASSERT(block == allocData->fHeader);
197 : }
198 :
199 0 : prev = block;
200 0 : } while ((block = block->fNext));
201 0 : SkASSERT(allocCount == fAllocationCnt);
202 0 : SkASSERT(fAllocationCnt == fAllocatedIDs.count());
203 0 : SkASSERT(prev == fTail);
204 0 : SkASSERT(fAllocBlockCnt != 0 || fSize == 0);
205 : #endif
206 0 : }
|