2 * Copyright 2013 Google Inc.
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
8 #include "SkDiscardableMemory.h"
9 #include "SkDiscardableMemoryPool.h"
11 #include "SkTInternalLList.h"
15 // A PoolDiscardableMemory is memory that is counted in a pool.
16 // A DiscardableMemoryPool is a pool of PoolDiscardableMemorys.
20 class PoolDiscardableMemory;
23 * This non-global pool can be used for unit tests to verify that the
26 class DiscardableMemoryPool : public SkDiscardableMemoryPool {
29 * Without mutex, will be not be thread safe.
31 DiscardableMemoryPool(size_t budget, SkBaseMutex* mutex = NULL);
32 virtual ~DiscardableMemoryPool();
34 virtual SkDiscardableMemory* create(size_t bytes) SK_OVERRIDE;
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; }
40 /** purges all unlocked DMs */
41 virtual void dumpPool() SK_OVERRIDE;
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;
51 #endif // SK_LAZY_CACHE_STATS
57 SkTInternalLList<PoolDiscardableMemory> fList;
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);
68 friend class PoolDiscardableMemory;
70 typedef SkDiscardableMemory::Factory INHERITED;
74 * A PoolDiscardableMemory is a SkDiscardableMemory that relies on
75 * a DiscardableMemoryPool object to manage the memory.
77 class PoolDiscardableMemory : public SkDiscardableMemory {
79 PoolDiscardableMemory(DiscardableMemoryPool* pool,
80 void* pointer, size_t bytes);
81 virtual ~PoolDiscardableMemory();
82 virtual bool lock() SK_OVERRIDE;
83 virtual void* data() SK_OVERRIDE;
84 virtual void unlock() SK_OVERRIDE;
85 friend class DiscardableMemoryPool;
87 SK_DECLARE_INTERNAL_LLIST_INTERFACE(PoolDiscardableMemory);
88 DiscardableMemoryPool* const fPool;
94 PoolDiscardableMemory::PoolDiscardableMemory(DiscardableMemoryPool* pool,
101 SkASSERT(fPool != NULL);
102 SkASSERT(fPointer != NULL);
103 SkASSERT(fBytes > 0);
107 PoolDiscardableMemory::~PoolDiscardableMemory() {
108 SkASSERT(!fLocked); // contract for SkDiscardableMemory
113 bool PoolDiscardableMemory::lock() {
114 SkASSERT(!fLocked); // contract for SkDiscardableMemory
115 return fPool->lock(this);
118 void* PoolDiscardableMemory::data() {
119 SkASSERT(fLocked); // contract for SkDiscardableMemory
123 void PoolDiscardableMemory::unlock() {
124 SkASSERT(fLocked); // contract for SkDiscardableMemory
128 ////////////////////////////////////////////////////////////////////////////////
130 DiscardableMemoryPool::DiscardableMemoryPool(size_t budget,
135 #if SK_LAZY_CACHE_STATS
138 #endif // SK_LAZY_CACHE_STATS
140 DiscardableMemoryPool::~DiscardableMemoryPool() {
141 // PoolDiscardableMemory objects that belong to this pool are
142 // always deleted before deleting this pool since each one has a
144 SkASSERT(fList.isEmpty());
147 void DiscardableMemoryPool::dumpDownTo(size_t budget) {
148 // assert((NULL = fMutex) || fMutex->isLocked());
149 // TODO(halcanary) implement bool fMutex::isLocked().
150 // WARNING: only call this function after aquiring lock.
151 if (fUsed <= budget) {
154 typedef SkTInternalLList<PoolDiscardableMemory>::Iter Iter;
156 PoolDiscardableMemory* cur = iter.init(fList, Iter::kTail_IterStart);
157 while ((fUsed > budget) && (NULL != cur)) {
159 PoolDiscardableMemory* dm = cur;
160 SkASSERT(dm->fPointer != NULL);
161 sk_free(dm->fPointer);
163 SkASSERT(fUsed >= dm->fBytes);
166 // Purged DMs are taken out of the list. This saves times
167 // looking them up. Purged DMs are NOT deleted.
175 SkDiscardableMemory* DiscardableMemoryPool::create(size_t bytes) {
176 void* addr = sk_malloc_flags(bytes, 0);
180 PoolDiscardableMemory* dm = SkNEW_ARGS(PoolDiscardableMemory,
181 (this, addr, bytes));
182 SkAutoMutexAcquire autoMutexAcquire(fMutex);
185 this->dumpDownTo(fBudget);
189 void DiscardableMemoryPool::free(PoolDiscardableMemory* dm) {
190 // This is called by dm's destructor.
191 if (dm->fPointer != NULL) {
192 SkAutoMutexAcquire autoMutexAcquire(fMutex);
193 sk_free(dm->fPointer);
195 SkASSERT(fUsed >= dm->fBytes);
199 SkASSERT(!fList.isInList(dm));
203 bool DiscardableMemoryPool::lock(PoolDiscardableMemory* dm) {
204 SkASSERT(dm != NULL);
205 if (NULL == dm->fPointer) {
206 #if SK_LAZY_CACHE_STATS
207 SkAutoMutexAcquire autoMutexAcquire(fMutex);
209 #endif // SK_LAZY_CACHE_STATS
212 SkAutoMutexAcquire autoMutexAcquire(fMutex);
213 if (NULL == dm->fPointer) {
214 // May have been purged while waiting for lock.
215 #if SK_LAZY_CACHE_STATS
217 #endif // SK_LAZY_CACHE_STATS
223 #if SK_LAZY_CACHE_STATS
225 #endif // SK_LAZY_CACHE_STATS
229 void DiscardableMemoryPool::unlock(PoolDiscardableMemory* dm) {
230 SkASSERT(dm != NULL);
231 SkAutoMutexAcquire autoMutexAcquire(fMutex);
233 this->dumpDownTo(fBudget);
236 size_t DiscardableMemoryPool::getRAMUsed() {
239 void DiscardableMemoryPool::setRAMBudget(size_t budget) {
240 SkAutoMutexAcquire autoMutexAcquire(fMutex);
242 this->dumpDownTo(fBudget);
244 void DiscardableMemoryPool::dumpPool() {
245 SkAutoMutexAcquire autoMutexAcquire(fMutex);
249 ////////////////////////////////////////////////////////////////////////////////
250 SK_DECLARE_STATIC_MUTEX(gMutex);
251 SkDiscardableMemoryPool* gPool = NULL;
252 void create_global_pool(int) {
253 SkASSERT(NULL == gPool);
254 gPool = SkDiscardableMemoryPool::Create(
255 SK_DEFAULT_GLOBAL_DISCARDABLE_MEMORY_POOL_SIZE, &gMutex);
257 void cleanup_global_pool() {
262 SkDiscardableMemoryPool* SkDiscardableMemoryPool::Create(
263 size_t size, SkBaseMutex* mutex) {
264 return SkNEW_ARGS(DiscardableMemoryPool, (size, mutex));
267 SkDiscardableMemoryPool* SkGetGlobalDiscardableMemoryPool() {
268 SK_DECLARE_STATIC_ONCE(create_pool_once);
269 SkOnce(&create_pool_once, create_global_pool, 0, &cleanup_global_pool);
270 SkASSERT(NULL != gPool);
274 ////////////////////////////////////////////////////////////////////////////////