blob: db2754e473ae37a736ac23a6a5d2957be10e8238 [file] [log] [blame]
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +00001/*
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
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +00008#include "SkDiscardableMemory.h"
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +00009#include "SkDiscardableMemoryPool.h"
mtklein78358bf2014-06-02 08:44:27 -070010#include "SkLazyPtr.h"
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +000011#include "SkTInternalLList.h"
12#include "SkThread.h"
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +000013
14// Note:
15// A PoolDiscardableMemory is memory that is counted in a pool.
16// A DiscardableMemoryPool is a pool of PoolDiscardableMemorys.
17
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +000018namespace {
19
20class PoolDiscardableMemory;
21
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +000022/**
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +000023 * This non-global pool can be used for unit tests to verify that the
24 * pool works.
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +000025 */
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +000026class DiscardableMemoryPool : public SkDiscardableMemoryPool {
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +000027public:
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +000028 /**
29 * Without mutex, will be not be thread safe.
30 */
31 DiscardableMemoryPool(size_t budget, SkBaseMutex* mutex = NULL);
32 virtual ~DiscardableMemoryPool();
33
34 virtual SkDiscardableMemory* create(size_t bytes) SK_OVERRIDE;
35
36 virtual size_t getRAMUsed() SK_OVERRIDE;
37 virtual void setRAMBudget(size_t budget) SK_OVERRIDE;
38 virtual size_t getRAMBudget() SK_OVERRIDE { return fBudget; }
39
40 /** purges all unlocked DMs */
41 virtual void dumpPool() SK_OVERRIDE;
42
43 #if SK_LAZY_CACHE_STATS // Defined in SkDiscardableMemoryPool.h
44 virtual int getCacheHits() SK_OVERRIDE { return fCacheHits; }
45 virtual int getCacheMisses() SK_OVERRIDE { return fCacheMisses; }
46 virtual void resetCacheHitsAndMisses() SK_OVERRIDE {
47 fCacheHits = fCacheMisses = 0;
48 }
49 int fCacheHits;
50 int fCacheMisses;
51 #endif // SK_LAZY_CACHE_STATS
52
53private:
54 SkBaseMutex* fMutex;
55 size_t fBudget;
56 size_t fUsed;
57 SkTInternalLList<PoolDiscardableMemory> fList;
58
59 /** Function called to free memory if needed */
60 void dumpDownTo(size_t budget);
61 /** called by DiscardableMemoryPool upon destruction */
62 void free(PoolDiscardableMemory* dm);
63 /** called by DiscardableMemoryPool::lock() */
64 bool lock(PoolDiscardableMemory* dm);
65 /** called by DiscardableMemoryPool::unlock() */
66 void unlock(PoolDiscardableMemory* dm);
67
68 friend class PoolDiscardableMemory;
69
70 typedef SkDiscardableMemory::Factory INHERITED;
71};
72
73/**
74 * A PoolDiscardableMemory is a SkDiscardableMemory that relies on
75 * a DiscardableMemoryPool object to manage the memory.
76 */
77class PoolDiscardableMemory : public SkDiscardableMemory {
78public:
79 PoolDiscardableMemory(DiscardableMemoryPool* pool,
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +000080 void* pointer, size_t bytes);
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +000081 virtual ~PoolDiscardableMemory();
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +000082 virtual bool lock() SK_OVERRIDE;
83 virtual void* data() SK_OVERRIDE;
84 virtual void unlock() SK_OVERRIDE;
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +000085 friend class DiscardableMemoryPool;
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +000086private:
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +000087 SK_DECLARE_INTERNAL_LLIST_INTERFACE(PoolDiscardableMemory);
88 DiscardableMemoryPool* const fPool;
89 bool fLocked;
90 void* fPointer;
91 const size_t fBytes;
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +000092};
93
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +000094PoolDiscardableMemory::PoolDiscardableMemory(DiscardableMemoryPool* pool,
95 void* pointer,
96 size_t bytes)
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +000097 : fPool(pool)
98 , fLocked(true)
99 , fPointer(pointer)
100 , fBytes(bytes) {
101 SkASSERT(fPool != NULL);
102 SkASSERT(fPointer != NULL);
103 SkASSERT(fBytes > 0);
104 fPool->ref();
105}
106
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000107PoolDiscardableMemory::~PoolDiscardableMemory() {
reed@google.com1d0654f2013-12-12 22:37:32 +0000108 SkASSERT(!fLocked); // contract for SkDiscardableMemory
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000109 fPool->free(this);
110 fPool->unref();
111}
112
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000113bool PoolDiscardableMemory::lock() {
reed@google.com1d0654f2013-12-12 22:37:32 +0000114 SkASSERT(!fLocked); // contract for SkDiscardableMemory
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000115 return fPool->lock(this);
116}
117
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000118void* PoolDiscardableMemory::data() {
reed@google.com1d0654f2013-12-12 22:37:32 +0000119 SkASSERT(fLocked); // contract for SkDiscardableMemory
120 return fPointer;
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000121}
122
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000123void PoolDiscardableMemory::unlock() {
reed@google.com1d0654f2013-12-12 22:37:32 +0000124 SkASSERT(fLocked); // contract for SkDiscardableMemory
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000125 fPool->unlock(this);
126}
127
128////////////////////////////////////////////////////////////////////////////////
129
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000130DiscardableMemoryPool::DiscardableMemoryPool(size_t budget,
131 SkBaseMutex* mutex)
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000132 : fMutex(mutex)
133 , fBudget(budget)
134 , fUsed(0) {
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000135 #if SK_LAZY_CACHE_STATS
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000136 fCacheHits = 0;
137 fCacheMisses = 0;
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000138 #endif // SK_LAZY_CACHE_STATS
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000139}
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000140DiscardableMemoryPool::~DiscardableMemoryPool() {
141 // PoolDiscardableMemory objects that belong to this pool are
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000142 // always deleted before deleting this pool since each one has a
143 // ref to the pool.
144 SkASSERT(fList.isEmpty());
145}
146
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000147void DiscardableMemoryPool::dumpDownTo(size_t budget) {
halcanary9b790662014-06-11 09:40:23 -0700148 if (fMutex != NULL) {
149 fMutex->assertHeld();
150 }
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000151 if (fUsed <= budget) {
152 return;
153 }
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000154 typedef SkTInternalLList<PoolDiscardableMemory>::Iter Iter;
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000155 Iter iter;
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000156 PoolDiscardableMemory* cur = iter.init(fList, Iter::kTail_IterStart);
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000157 while ((fUsed > budget) && (NULL != cur)) {
158 if (!cur->fLocked) {
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000159 PoolDiscardableMemory* dm = cur;
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000160 SkASSERT(dm->fPointer != NULL);
161 sk_free(dm->fPointer);
162 dm->fPointer = NULL;
163 SkASSERT(fUsed >= dm->fBytes);
164 fUsed -= dm->fBytes;
165 cur = iter.prev();
166 // Purged DMs are taken out of the list. This saves times
167 // looking them up. Purged DMs are NOT deleted.
168 fList.remove(dm);
169 } else {
170 cur = iter.prev();
171 }
172 }
173}
174
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000175SkDiscardableMemory* DiscardableMemoryPool::create(size_t bytes) {
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000176 void* addr = sk_malloc_flags(bytes, 0);
177 if (NULL == addr) {
178 return NULL;
179 }
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000180 PoolDiscardableMemory* dm = SkNEW_ARGS(PoolDiscardableMemory,
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000181 (this, addr, bytes));
182 SkAutoMutexAcquire autoMutexAcquire(fMutex);
183 fList.addToHead(dm);
184 fUsed += bytes;
185 this->dumpDownTo(fBudget);
186 return dm;
187}
188
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000189void DiscardableMemoryPool::free(PoolDiscardableMemory* dm) {
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000190 // This is called by dm's destructor.
191 if (dm->fPointer != NULL) {
192 SkAutoMutexAcquire autoMutexAcquire(fMutex);
193 sk_free(dm->fPointer);
194 dm->fPointer = NULL;
195 SkASSERT(fUsed >= dm->fBytes);
196 fUsed -= dm->fBytes;
197 fList.remove(dm);
198 } else {
199 SkASSERT(!fList.isInList(dm));
200 }
201}
202
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000203bool DiscardableMemoryPool::lock(PoolDiscardableMemory* dm) {
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000204 SkASSERT(dm != NULL);
205 if (NULL == dm->fPointer) {
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000206 #if SK_LAZY_CACHE_STATS
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000207 SkAutoMutexAcquire autoMutexAcquire(fMutex);
208 ++fCacheMisses;
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000209 #endif // SK_LAZY_CACHE_STATS
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000210 return false;
211 }
212 SkAutoMutexAcquire autoMutexAcquire(fMutex);
213 if (NULL == dm->fPointer) {
214 // May have been purged while waiting for lock.
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000215 #if SK_LAZY_CACHE_STATS
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000216 ++fCacheMisses;
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000217 #endif // SK_LAZY_CACHE_STATS
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000218 return false;
219 }
220 dm->fLocked = true;
221 fList.remove(dm);
222 fList.addToHead(dm);
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000223 #if SK_LAZY_CACHE_STATS
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000224 ++fCacheHits;
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000225 #endif // SK_LAZY_CACHE_STATS
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000226 return true;
227}
228
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000229void DiscardableMemoryPool::unlock(PoolDiscardableMemory* dm) {
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000230 SkASSERT(dm != NULL);
231 SkAutoMutexAcquire autoMutexAcquire(fMutex);
232 dm->fLocked = false;
233 this->dumpDownTo(fBudget);
234}
235
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000236size_t DiscardableMemoryPool::getRAMUsed() {
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000237 return fUsed;
238}
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000239void DiscardableMemoryPool::setRAMBudget(size_t budget) {
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000240 SkAutoMutexAcquire autoMutexAcquire(fMutex);
241 fBudget = budget;
242 this->dumpDownTo(fBudget);
243}
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000244void DiscardableMemoryPool::dumpPool() {
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000245 SkAutoMutexAcquire autoMutexAcquire(fMutex);
246 this->dumpDownTo(0);
247}
248
249////////////////////////////////////////////////////////////////////////////////
250SK_DECLARE_STATIC_MUTEX(gMutex);
mtklein78358bf2014-06-02 08:44:27 -0700251SkDiscardableMemoryPool* create_global_pool() {
252 return SkDiscardableMemoryPool::Create(SK_DEFAULT_GLOBAL_DISCARDABLE_MEMORY_POOL_SIZE,
253 &gMutex);
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000254}
mtklein78358bf2014-06-02 08:44:27 -0700255
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000256} // namespace
257
mtklein78358bf2014-06-02 08:44:27 -0700258SkDiscardableMemoryPool* SkDiscardableMemoryPool::Create(size_t size, SkBaseMutex* mutex) {
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000259 return SkNEW_ARGS(DiscardableMemoryPool, (size, mutex));
260}
261
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000262SkDiscardableMemoryPool* SkGetGlobalDiscardableMemoryPool() {
mtklein78358bf2014-06-02 08:44:27 -0700263 SK_DECLARE_STATIC_LAZY_PTR(SkDiscardableMemoryPool, global, create_global_pool);
264 return global.get();
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000265}
266
commit-bot@chromium.org7e2b0f32014-05-28 18:51:03 +0000267// defined in SkImageGenerator.h
268void SkPurgeGlobalDiscardableMemoryPool() {
269 SkGetGlobalDiscardableMemoryPool()->dumpPool();
270}
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000271////////////////////////////////////////////////////////////////////////////////