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