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 "include/private/SkMalloc.h"
9 #include "include/private/SkMutex.h"
10 #include "include/private/SkTemplates.h"
11 #include "src/core/SkDiscardableMemory.h"
12 #include "src/core/SkTInternalLList.h"
13 #include "src/lazy/SkDiscardableMemoryPool.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 DiscardableMemoryPool(size_t budget);
30 ~DiscardableMemoryPool() override;
31
32 std::unique_ptr<SkDiscardableMemory> make(size_t bytes);
create(size_t bytes)33 SkDiscardableMemory* create(size_t bytes) override {
34 return this->make(bytes).release(); // TODO: change API
35 }
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 SkMutex 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 removeFromPool(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 using INHERITED = SkDiscardableMemory::Factory;
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(sk_sp<DiscardableMemoryPool> pool, SkAutoFree pointer, size_t bytes);
81 ~PoolDiscardableMemory() override;
82 bool lock() override;
83 void* data() override;
84 void unlock() override;
85 friend class DiscardableMemoryPool;
86 private:
87 SK_DECLARE_INTERNAL_LLIST_INTERFACE(PoolDiscardableMemory);
88 sk_sp<DiscardableMemoryPool> fPool;
89 bool fLocked;
90 SkAutoFree fPointer;
91 const size_t fBytes;
92 };
93
PoolDiscardableMemory(sk_sp<DiscardableMemoryPool> pool,SkAutoFree pointer,size_t bytes)94 PoolDiscardableMemory::PoolDiscardableMemory(sk_sp<DiscardableMemoryPool> pool,
95 SkAutoFree pointer,
96 size_t bytes)
97 : fPool(std::move(pool)), fLocked(true), fPointer(std::move(pointer)), fBytes(bytes) {
98 SkASSERT(fPool != nullptr);
99 SkASSERT(fPointer != nullptr);
100 SkASSERT(fBytes > 0);
101 }
102
~PoolDiscardableMemory()103 PoolDiscardableMemory::~PoolDiscardableMemory() {
104 SkASSERT(!fLocked); // contract for SkDiscardableMemory
105 fPool->removeFromPool(this);
106 }
107
lock()108 bool PoolDiscardableMemory::lock() {
109 SkASSERT(!fLocked); // contract for SkDiscardableMemory
110 return fPool->lock(this);
111 }
112
data()113 void* PoolDiscardableMemory::data() {
114 SkASSERT(fLocked); // contract for SkDiscardableMemory
115 return fPointer.get();
116 }
117
unlock()118 void PoolDiscardableMemory::unlock() {
119 SkASSERT(fLocked); // contract for SkDiscardableMemory
120 fPool->unlock(this);
121 }
122
123 ////////////////////////////////////////////////////////////////////////////////
124
DiscardableMemoryPool(size_t budget)125 DiscardableMemoryPool::DiscardableMemoryPool(size_t budget)
126 : fBudget(budget)
127 , fUsed(0) {
128 #if SK_LAZY_CACHE_STATS
129 fCacheHits = 0;
130 fCacheMisses = 0;
131 #endif // SK_LAZY_CACHE_STATS
132 }
~DiscardableMemoryPool()133 DiscardableMemoryPool::~DiscardableMemoryPool() {
134 // PoolDiscardableMemory objects that belong to this pool are
135 // always deleted before deleting this pool since each one has a
136 // ref to the pool.
137 SkASSERT(fList.isEmpty());
138 }
139
dumpDownTo(size_t budget)140 void DiscardableMemoryPool::dumpDownTo(size_t budget) {
141 fMutex.assertHeld();
142 if (fUsed <= budget) {
143 return;
144 }
145 using Iter = SkTInternalLList<PoolDiscardableMemory>::Iter;
146 Iter iter;
147 PoolDiscardableMemory* cur = iter.init(fList, Iter::kTail_IterStart);
148 while ((fUsed > budget) && (cur)) {
149 if (!cur->fLocked) {
150 PoolDiscardableMemory* dm = cur;
151 SkASSERT(dm->fPointer != nullptr);
152 dm->fPointer = nullptr;
153 SkASSERT(fUsed >= dm->fBytes);
154 fUsed -= dm->fBytes;
155 cur = iter.prev();
156 // Purged DMs are taken out of the list. This saves times
157 // looking them up. Purged DMs are NOT deleted.
158 fList.remove(dm);
159 } else {
160 cur = iter.prev();
161 }
162 }
163 }
164
make(size_t bytes)165 std::unique_ptr<SkDiscardableMemory> DiscardableMemoryPool::make(size_t bytes) {
166 SkAutoFree addr(sk_malloc_canfail(bytes));
167 if (nullptr == addr) {
168 return nullptr;
169 }
170 auto dm = std::make_unique<PoolDiscardableMemory>(sk_ref_sp(this), std::move(addr), bytes);
171 SkAutoMutexExclusive autoMutexAcquire(fMutex);
172 fList.addToHead(dm.get());
173 fUsed += bytes;
174 this->dumpDownTo(fBudget);
175 return std::move(dm);
176 }
177
removeFromPool(PoolDiscardableMemory * dm)178 void DiscardableMemoryPool::removeFromPool(PoolDiscardableMemory* dm) {
179 SkAutoMutexExclusive autoMutexAcquire(fMutex);
180 // This is called by dm's destructor.
181 if (dm->fPointer != nullptr) {
182 SkASSERT(fUsed >= dm->fBytes);
183 fUsed -= dm->fBytes;
184 fList.remove(dm);
185 } else {
186 SkASSERT(!fList.isInList(dm));
187 }
188 }
189
lock(PoolDiscardableMemory * dm)190 bool DiscardableMemoryPool::lock(PoolDiscardableMemory* dm) {
191 SkASSERT(dm != nullptr);
192 SkAutoMutexExclusive autoMutexAcquire(fMutex);
193 if (nullptr == dm->fPointer) {
194 // May have been purged while waiting for lock.
195 #if SK_LAZY_CACHE_STATS
196 ++fCacheMisses;
197 #endif // SK_LAZY_CACHE_STATS
198 return false;
199 }
200 dm->fLocked = true;
201 fList.remove(dm);
202 fList.addToHead(dm);
203 #if SK_LAZY_CACHE_STATS
204 ++fCacheHits;
205 #endif // SK_LAZY_CACHE_STATS
206 return true;
207 }
208
unlock(PoolDiscardableMemory * dm)209 void DiscardableMemoryPool::unlock(PoolDiscardableMemory* dm) {
210 SkASSERT(dm != nullptr);
211 SkAutoMutexExclusive autoMutexAcquire(fMutex);
212 dm->fLocked = false;
213 this->dumpDownTo(fBudget);
214 }
215
getRAMUsed()216 size_t DiscardableMemoryPool::getRAMUsed() {
217 return fUsed;
218 }
setRAMBudget(size_t budget)219 void DiscardableMemoryPool::setRAMBudget(size_t budget) {
220 SkAutoMutexExclusive autoMutexAcquire(fMutex);
221 fBudget = budget;
222 this->dumpDownTo(fBudget);
223 }
dumpPool()224 void DiscardableMemoryPool::dumpPool() {
225 SkAutoMutexExclusive autoMutexAcquire(fMutex);
226 this->dumpDownTo(0);
227 }
228
229 } // namespace
230
Make(size_t size)231 sk_sp<SkDiscardableMemoryPool> SkDiscardableMemoryPool::Make(size_t size) {
232 return sk_make_sp<DiscardableMemoryPool>(size);
233 }
234
SkGetGlobalDiscardableMemoryPool()235 SkDiscardableMemoryPool* SkGetGlobalDiscardableMemoryPool() {
236 // Intentionally leak this global pool.
237 static SkDiscardableMemoryPool* global =
238 new DiscardableMemoryPool(SK_DEFAULT_GLOBAL_DISCARDABLE_MEMORY_POOL_SIZE);
239 return global;
240 }
241