1 /*
2 * Copyright 2013 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 "SkDiscardableMemoryPool.h"
9 #include "SkDiscardableMemory.h"
10 #include "SkMakeUnique.h"
11 #include "SkMalloc.h"
12 #include "SkMutex.h"
13 #include "SkTInternalLList.h"
14 #include "SkTemplates.h"
15
16 // Note:
17 // A PoolDiscardableMemory is memory that is counted in a pool.
18 // A DiscardableMemoryPool is a pool of PoolDiscardableMemorys.
19
20 namespace {
21
22 class PoolDiscardableMemory;
23
24 /**
25 * This non-global pool can be used for unit tests to verify that the
26 * pool works.
27 */
28 class DiscardableMemoryPool : public SkDiscardableMemoryPool {
29 public:
30 DiscardableMemoryPool(size_t budget);
31 ~DiscardableMemoryPool() override;
32
33 std::unique_ptr<SkDiscardableMemory> make(size_t bytes);
create(size_t bytes)34 SkDiscardableMemory* create(size_t bytes) override {
35 return this->make(bytes).release(); // TODO: change API
36 }
37
38 size_t getRAMUsed() override;
39 void setRAMBudget(size_t budget) override;
getRAMBudget()40 size_t getRAMBudget() override { return fBudget; }
41
42 /** purges all unlocked DMs */
43 void dumpPool() override;
44
45 #if SK_LAZY_CACHE_STATS // Defined in SkDiscardableMemoryPool.h
getCacheHits()46 int getCacheHits() override { return fCacheHits; }
getCacheMisses()47 int getCacheMisses() override { return fCacheMisses; }
resetCacheHitsAndMisses()48 void resetCacheHitsAndMisses() override {
49 fCacheHits = fCacheMisses = 0;
50 }
51 int fCacheHits;
52 int fCacheMisses;
53 #endif // SK_LAZY_CACHE_STATS
54
55 private:
56 SkMutex fMutex;
57 size_t fBudget;
58 size_t fUsed;
59 SkTInternalLList<PoolDiscardableMemory> fList;
60
61 /** Function called to free memory if needed */
62 void dumpDownTo(size_t budget);
63 /** called by DiscardableMemoryPool upon destruction */
64 void removeFromPool(PoolDiscardableMemory* dm);
65 /** called by DiscardableMemoryPool::lock() */
66 bool lock(PoolDiscardableMemory* dm);
67 /** called by DiscardableMemoryPool::unlock() */
68 void unlock(PoolDiscardableMemory* dm);
69
70 friend class PoolDiscardableMemory;
71
72 typedef SkDiscardableMemory::Factory INHERITED;
73 };
74
75 /**
76 * A PoolDiscardableMemory is a SkDiscardableMemory that relies on
77 * a DiscardableMemoryPool object to manage the memory.
78 */
79 class PoolDiscardableMemory : public SkDiscardableMemory {
80 public:
81 PoolDiscardableMemory(sk_sp<DiscardableMemoryPool> pool, SkAutoFree pointer, size_t bytes);
82 ~PoolDiscardableMemory() override;
83 bool lock() override;
84 void* data() override;
85 void unlock() override;
86 friend class DiscardableMemoryPool;
87 private:
88 SK_DECLARE_INTERNAL_LLIST_INTERFACE(PoolDiscardableMemory);
89 sk_sp<DiscardableMemoryPool> fPool;
90 bool fLocked;
91 SkAutoFree fPointer;
92 const size_t fBytes;
93 };
94
PoolDiscardableMemory(sk_sp<DiscardableMemoryPool> pool,SkAutoFree pointer,size_t bytes)95 PoolDiscardableMemory::PoolDiscardableMemory(sk_sp<DiscardableMemoryPool> pool,
96 SkAutoFree pointer,
97 size_t bytes)
98 : fPool(std::move(pool)), fLocked(true), fPointer(std::move(pointer)), fBytes(bytes) {
99 SkASSERT(fPool != nullptr);
100 SkASSERT(fPointer != nullptr);
101 SkASSERT(fBytes > 0);
102 }
103
~PoolDiscardableMemory()104 PoolDiscardableMemory::~PoolDiscardableMemory() {
105 SkASSERT(!fLocked); // contract for SkDiscardableMemory
106 fPool->removeFromPool(this);
107 }
108
lock()109 bool PoolDiscardableMemory::lock() {
110 SkASSERT(!fLocked); // contract for SkDiscardableMemory
111 return fPool->lock(this);
112 }
113
data()114 void* PoolDiscardableMemory::data() {
115 SkASSERT(fLocked); // contract for SkDiscardableMemory
116 return fPointer.get();
117 }
118
unlock()119 void PoolDiscardableMemory::unlock() {
120 SkASSERT(fLocked); // contract for SkDiscardableMemory
121 fPool->unlock(this);
122 }
123
124 ////////////////////////////////////////////////////////////////////////////////
125
DiscardableMemoryPool(size_t budget)126 DiscardableMemoryPool::DiscardableMemoryPool(size_t budget)
127 : fBudget(budget)
128 , fUsed(0) {
129 #if SK_LAZY_CACHE_STATS
130 fCacheHits = 0;
131 fCacheMisses = 0;
132 #endif // SK_LAZY_CACHE_STATS
133 }
~DiscardableMemoryPool()134 DiscardableMemoryPool::~DiscardableMemoryPool() {
135 // PoolDiscardableMemory objects that belong to this pool are
136 // always deleted before deleting this pool since each one has a
137 // ref to the pool.
138 SkASSERT(fList.isEmpty());
139 }
140
dumpDownTo(size_t budget)141 void DiscardableMemoryPool::dumpDownTo(size_t budget) {
142 fMutex.assertHeld();
143 if (fUsed <= budget) {
144 return;
145 }
146 using Iter = SkTInternalLList<PoolDiscardableMemory>::Iter;
147 Iter iter;
148 PoolDiscardableMemory* cur = iter.init(fList, Iter::kTail_IterStart);
149 while ((fUsed > budget) && (cur)) {
150 if (!cur->fLocked) {
151 PoolDiscardableMemory* dm = cur;
152 SkASSERT(dm->fPointer != nullptr);
153 dm->fPointer = nullptr;
154 SkASSERT(fUsed >= dm->fBytes);
155 fUsed -= dm->fBytes;
156 cur = iter.prev();
157 // Purged DMs are taken out of the list. This saves times
158 // looking them up. Purged DMs are NOT deleted.
159 fList.remove(dm);
160 } else {
161 cur = iter.prev();
162 }
163 }
164 }
165
make(size_t bytes)166 std::unique_ptr<SkDiscardableMemory> DiscardableMemoryPool::make(size_t bytes) {
167 SkAutoFree addr(sk_malloc_flags(bytes, 0));
168 if (nullptr == addr) {
169 return nullptr;
170 }
171 auto dm = skstd::make_unique<PoolDiscardableMemory>(sk_ref_sp(this), std::move(addr), bytes);
172 SkAutoMutexAcquire autoMutexAcquire(fMutex);
173 fList.addToHead(dm.get());
174 fUsed += bytes;
175 this->dumpDownTo(fBudget);
176 return std::move(dm);
177 }
178
removeFromPool(PoolDiscardableMemory * dm)179 void DiscardableMemoryPool::removeFromPool(PoolDiscardableMemory* dm) {
180 SkAutoMutexAcquire autoMutexAcquire(fMutex);
181 // This is called by dm's destructor.
182 if (dm->fPointer != nullptr) {
183 SkASSERT(fUsed >= dm->fBytes);
184 fUsed -= dm->fBytes;
185 fList.remove(dm);
186 } else {
187 SkASSERT(!fList.isInList(dm));
188 }
189 }
190
lock(PoolDiscardableMemory * dm)191 bool DiscardableMemoryPool::lock(PoolDiscardableMemory* dm) {
192 SkASSERT(dm != nullptr);
193 SkAutoMutexAcquire autoMutexAcquire(fMutex);
194 if (nullptr == dm->fPointer) {
195 // May have been purged while waiting for lock.
196 #if SK_LAZY_CACHE_STATS
197 ++fCacheMisses;
198 #endif // SK_LAZY_CACHE_STATS
199 return false;
200 }
201 dm->fLocked = true;
202 fList.remove(dm);
203 fList.addToHead(dm);
204 #if SK_LAZY_CACHE_STATS
205 ++fCacheHits;
206 #endif // SK_LAZY_CACHE_STATS
207 return true;
208 }
209
unlock(PoolDiscardableMemory * dm)210 void DiscardableMemoryPool::unlock(PoolDiscardableMemory* dm) {
211 SkASSERT(dm != nullptr);
212 SkAutoMutexAcquire autoMutexAcquire(fMutex);
213 dm->fLocked = false;
214 this->dumpDownTo(fBudget);
215 }
216
getRAMUsed()217 size_t DiscardableMemoryPool::getRAMUsed() {
218 return fUsed;
219 }
setRAMBudget(size_t budget)220 void DiscardableMemoryPool::setRAMBudget(size_t budget) {
221 SkAutoMutexAcquire autoMutexAcquire(fMutex);
222 fBudget = budget;
223 this->dumpDownTo(fBudget);
224 }
dumpPool()225 void DiscardableMemoryPool::dumpPool() {
226 SkAutoMutexAcquire autoMutexAcquire(fMutex);
227 this->dumpDownTo(0);
228 }
229
230 } // namespace
231
Make(size_t size)232 sk_sp<SkDiscardableMemoryPool> SkDiscardableMemoryPool::Make(size_t size) {
233 return sk_make_sp<DiscardableMemoryPool>(size);
234 }
235
SkGetGlobalDiscardableMemoryPool()236 SkDiscardableMemoryPool* SkGetGlobalDiscardableMemoryPool() {
237 // Intentionally leak this global pool.
238 static SkDiscardableMemoryPool* global =
239 new DiscardableMemoryPool(SK_DEFAULT_GLOBAL_DISCARDABLE_MEMORY_POOL_SIZE);
240 return global;
241 }
242