SkDiscardableMemoryPool.cpp revision f71e8fd0c5d2da9795d00a5b81b716ae585be14a
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 "SkOnce.h" 11#include "SkTInternalLList.h" 12#include "SkThread.h" 13 14// Note: 15// A PoolDiscardableMemory is memory that is counted in a pool. 16// A DiscardableMemoryPool is a pool of PoolDiscardableMemorys. 17 18namespace { 19 20class PoolDiscardableMemory; 21 22/** 23 * This non-global pool can be used for unit tests to verify that the 24 * pool works. 25 */ 26class DiscardableMemoryPool : public SkDiscardableMemoryPool { 27public: 28 /** 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, 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; 86private: 87 SK_DECLARE_INTERNAL_LLIST_INTERFACE(PoolDiscardableMemory); 88 DiscardableMemoryPool* const fPool; 89 bool fLocked; 90 void* fPointer; 91 const size_t fBytes; 92}; 93 94PoolDiscardableMemory::PoolDiscardableMemory(DiscardableMemoryPool* pool, 95 void* pointer, 96 size_t bytes) 97 : 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 107PoolDiscardableMemory::~PoolDiscardableMemory() { 108 SkASSERT(!fLocked); // contract for SkDiscardableMemory 109 fPool->free(this); 110 fPool->unref(); 111} 112 113bool PoolDiscardableMemory::lock() { 114 SkASSERT(!fLocked); // contract for SkDiscardableMemory 115 return fPool->lock(this); 116} 117 118void* PoolDiscardableMemory::data() { 119 SkASSERT(fLocked); // contract for SkDiscardableMemory 120 return fPointer; 121} 122 123void PoolDiscardableMemory::unlock() { 124 SkASSERT(fLocked); // contract for SkDiscardableMemory 125 fPool->unlock(this); 126} 127 128//////////////////////////////////////////////////////////////////////////////// 129 130DiscardableMemoryPool::DiscardableMemoryPool(size_t budget, 131 SkBaseMutex* mutex) 132 : fMutex(mutex) 133 , fBudget(budget) 134 , fUsed(0) { 135 #if SK_LAZY_CACHE_STATS 136 fCacheHits = 0; 137 fCacheMisses = 0; 138 #endif // SK_LAZY_CACHE_STATS 139} 140DiscardableMemoryPool::~DiscardableMemoryPool() { 141 // PoolDiscardableMemory objects that belong to this pool are 142 // always deleted before deleting this pool since each one has a 143 // ref to the pool. 144 SkASSERT(fList.isEmpty()); 145} 146 147void 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) { 152 return; 153 } 154 typedef SkTInternalLList<PoolDiscardableMemory>::Iter Iter; 155 Iter iter; 156 PoolDiscardableMemory* cur = iter.init(fList, Iter::kTail_IterStart); 157 while ((fUsed > budget) && (NULL != cur)) { 158 if (!cur->fLocked) { 159 PoolDiscardableMemory* dm = cur; 160 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 175SkDiscardableMemory* DiscardableMemoryPool::create(size_t bytes) { 176 void* addr = sk_malloc_flags(bytes, 0); 177 if (NULL == addr) { 178 return NULL; 179 } 180 PoolDiscardableMemory* dm = SkNEW_ARGS(PoolDiscardableMemory, 181 (this, addr, bytes)); 182 SkAutoMutexAcquire autoMutexAcquire(fMutex); 183 fList.addToHead(dm); 184 fUsed += bytes; 185 this->dumpDownTo(fBudget); 186 return dm; 187} 188 189void 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); 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 203bool DiscardableMemoryPool::lock(PoolDiscardableMemory* dm) { 204 SkASSERT(dm != NULL); 205 if (NULL == dm->fPointer) { 206 #if SK_LAZY_CACHE_STATS 207 SkAutoMutexAcquire autoMutexAcquire(fMutex); 208 ++fCacheMisses; 209 #endif // SK_LAZY_CACHE_STATS 210 return false; 211 } 212 SkAutoMutexAcquire autoMutexAcquire(fMutex); 213 if (NULL == dm->fPointer) { 214 // May have been purged while waiting for lock. 215 #if SK_LAZY_CACHE_STATS 216 ++fCacheMisses; 217 #endif // SK_LAZY_CACHE_STATS 218 return false; 219 } 220 dm->fLocked = true; 221 fList.remove(dm); 222 fList.addToHead(dm); 223 #if SK_LAZY_CACHE_STATS 224 ++fCacheHits; 225 #endif // SK_LAZY_CACHE_STATS 226 return true; 227} 228 229void DiscardableMemoryPool::unlock(PoolDiscardableMemory* dm) { 230 SkASSERT(dm != NULL); 231 SkAutoMutexAcquire autoMutexAcquire(fMutex); 232 dm->fLocked = false; 233 this->dumpDownTo(fBudget); 234} 235 236size_t DiscardableMemoryPool::getRAMUsed() { 237 return fUsed; 238} 239void DiscardableMemoryPool::setRAMBudget(size_t budget) { 240 SkAutoMutexAcquire autoMutexAcquire(fMutex); 241 fBudget = budget; 242 this->dumpDownTo(fBudget); 243} 244void DiscardableMemoryPool::dumpPool() { 245 SkAutoMutexAcquire autoMutexAcquire(fMutex); 246 this->dumpDownTo(0); 247} 248 249//////////////////////////////////////////////////////////////////////////////// 250SK_DECLARE_STATIC_MUTEX(gMutex); 251SkDiscardableMemoryPool* gPool = NULL; 252void create_global_pool(int) { 253 SkASSERT(NULL == gPool); 254 gPool = SkDiscardableMemoryPool::Create( 255 SK_DEFAULT_GLOBAL_DISCARDABLE_MEMORY_POOL_SIZE, &gMutex); 256} 257void cleanup_global_pool() { 258 gPool->unref(); 259} 260} // namespace 261 262SkDiscardableMemoryPool* SkDiscardableMemoryPool::Create( 263 size_t size, SkBaseMutex* mutex) { 264 return SkNEW_ARGS(DiscardableMemoryPool, (size, mutex)); 265} 266 267SkDiscardableMemoryPool* 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); 271 return gPool; 272} 273 274// defined in SkImageGenerator.h 275void SkPurgeGlobalDiscardableMemoryPool() { 276 SkGetGlobalDiscardableMemoryPool()->dumpPool(); 277} 278//////////////////////////////////////////////////////////////////////////////// 279