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