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