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