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 "SkLazyPtr.h"
12 #include "SkTInternalLList.h"
13 #include "SkThread.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 = NULL);
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 != NULL);
103     SkASSERT(fPointer != NULL);
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 != NULL) {
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 != NULL);
162             sk_free(dm->fPointer);
163             dm->fPointer = NULL;
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 (NULL == addr) {
179         return NULL;
180     }
181     PoolDiscardableMemory* dm = SkNEW_ARGS(PoolDiscardableMemory,
182                                              (this, addr, bytes));
183     SkAutoMutexAcquire autoMutexAcquire(fMutex);
184     fList.addToHead(dm);
185     fUsed += bytes;
186     this->dumpDownTo(fBudget);
187     return dm;
188 }
189 
free(PoolDiscardableMemory * dm)190 void DiscardableMemoryPool::free(PoolDiscardableMemory* dm) {
191     SkAutoMutexAcquire autoMutexAcquire(fMutex);
192     // This is called by dm's destructor.
193     if (dm->fPointer != NULL) {
194         sk_free(dm->fPointer);
195         dm->fPointer = NULL;
196         SkASSERT(fUsed >= dm->fBytes);
197         fUsed -= dm->fBytes;
198         fList.remove(dm);
199     } else {
200         SkASSERT(!fList.isInList(dm));
201     }
202 }
203 
lock(PoolDiscardableMemory * dm)204 bool DiscardableMemoryPool::lock(PoolDiscardableMemory* dm) {
205     SkASSERT(dm != NULL);
206     if (NULL == dm->fPointer) {
207         #if SK_LAZY_CACHE_STATS
208         SkAutoMutexAcquire autoMutexAcquire(fMutex);
209         ++fCacheMisses;
210         #endif  // SK_LAZY_CACHE_STATS
211         return false;
212     }
213     SkAutoMutexAcquire autoMutexAcquire(fMutex);
214     if (NULL == dm->fPointer) {
215         // May have been purged while waiting for lock.
216         #if SK_LAZY_CACHE_STATS
217         ++fCacheMisses;
218         #endif  // SK_LAZY_CACHE_STATS
219         return false;
220     }
221     dm->fLocked = true;
222     fList.remove(dm);
223     fList.addToHead(dm);
224     #if SK_LAZY_CACHE_STATS
225     ++fCacheHits;
226     #endif  // SK_LAZY_CACHE_STATS
227     return true;
228 }
229 
unlock(PoolDiscardableMemory * dm)230 void DiscardableMemoryPool::unlock(PoolDiscardableMemory* dm) {
231     SkASSERT(dm != NULL);
232     SkAutoMutexAcquire autoMutexAcquire(fMutex);
233     dm->fLocked = false;
234     this->dumpDownTo(fBudget);
235 }
236 
getRAMUsed()237 size_t DiscardableMemoryPool::getRAMUsed() {
238     return fUsed;
239 }
setRAMBudget(size_t budget)240 void DiscardableMemoryPool::setRAMBudget(size_t budget) {
241     SkAutoMutexAcquire autoMutexAcquire(fMutex);
242     fBudget = budget;
243     this->dumpDownTo(fBudget);
244 }
dumpPool()245 void DiscardableMemoryPool::dumpPool() {
246     SkAutoMutexAcquire autoMutexAcquire(fMutex);
247     this->dumpDownTo(0);
248 }
249 
250 ////////////////////////////////////////////////////////////////////////////////
251 SK_DECLARE_STATIC_MUTEX(gMutex);
create_global_pool()252 SkDiscardableMemoryPool* create_global_pool() {
253     return SkDiscardableMemoryPool::Create(SK_DEFAULT_GLOBAL_DISCARDABLE_MEMORY_POOL_SIZE,
254                                            &gMutex);
255 }
256 
257 }  // namespace
258 
Create(size_t size,SkBaseMutex * mutex)259 SkDiscardableMemoryPool* SkDiscardableMemoryPool::Create(size_t size, SkBaseMutex* mutex) {
260     return SkNEW_ARGS(DiscardableMemoryPool, (size, mutex));
261 }
262 
263 SK_DECLARE_STATIC_LAZY_PTR(SkDiscardableMemoryPool, global, create_global_pool);
264 
SkGetGlobalDiscardableMemoryPool()265 SkDiscardableMemoryPool* SkGetGlobalDiscardableMemoryPool() {
266     return global.get();
267 }
268 
269 ////////////////////////////////////////////////////////////////////////////////
270