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 virtual SkDiscardableMemory* create(size_t bytes) SK_OVERRIDE; 36 37 virtual size_t getRAMUsed() SK_OVERRIDE; 38 virtual void setRAMBudget(size_t budget) SK_OVERRIDE; 39 virtual size_t getRAMBudget() SK_OVERRIDE { return fBudget; } 40 41 /** purges all unlocked DMs */ 42 virtual void dumpPool() SK_OVERRIDE; 43 44 #if SK_LAZY_CACHE_STATS // Defined in SkDiscardableMemoryPool.h 45 virtual int getCacheHits() SK_OVERRIDE { return fCacheHits; } 46 virtual int getCacheMisses() SK_OVERRIDE { return fCacheMisses; } 47 virtual void resetCacheHitsAndMisses() SK_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 virtual bool lock() SK_OVERRIDE; 84 virtual void* data() SK_OVERRIDE; 85 virtual void unlock() SK_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 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 108 PoolDiscardableMemory::~PoolDiscardableMemory() { 109 SkASSERT(!fLocked); // contract for SkDiscardableMemory 110 fPool->free(this); 111 fPool->unref(); 112 } 113 114 bool PoolDiscardableMemory::lock() { 115 SkASSERT(!fLocked); // contract for SkDiscardableMemory 116 return fPool->lock(this); 117 } 118 119 void* PoolDiscardableMemory::data() { 120 SkASSERT(fLocked); // contract for SkDiscardableMemory 121 return fPointer; 122 } 123 124 void PoolDiscardableMemory::unlock() { 125 SkASSERT(fLocked); // contract for SkDiscardableMemory 126 fPool->unlock(this); 127 } 128 129 //////////////////////////////////////////////////////////////////////////////// 130 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 } 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 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 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 190 void DiscardableMemoryPool::free(PoolDiscardableMemory* dm) { 191 // This is called by dm's destructor. 192 if (dm->fPointer != NULL) { 193 SkAutoMutexAcquire autoMutexAcquire(fMutex); 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 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 230 void DiscardableMemoryPool::unlock(PoolDiscardableMemory* dm) { 231 SkASSERT(dm != NULL); 232 SkAutoMutexAcquire autoMutexAcquire(fMutex); 233 dm->fLocked = false; 234 this->dumpDownTo(fBudget); 235 } 236 237 size_t DiscardableMemoryPool::getRAMUsed() { 238 return fUsed; 239 } 240 void DiscardableMemoryPool::setRAMBudget(size_t budget) { 241 SkAutoMutexAcquire autoMutexAcquire(fMutex); 242 fBudget = budget; 243 this->dumpDownTo(fBudget); 244 } 245 void DiscardableMemoryPool::dumpPool() { 246 SkAutoMutexAcquire autoMutexAcquire(fMutex); 247 this->dumpDownTo(0); 248 } 249 250 //////////////////////////////////////////////////////////////////////////////// 251 SK_DECLARE_STATIC_MUTEX(gMutex); 252 SkDiscardableMemoryPool* create_global_pool() { 253 return SkDiscardableMemoryPool::Create(SK_DEFAULT_GLOBAL_DISCARDABLE_MEMORY_POOL_SIZE, 254 &gMutex); 255 } 256 257 } // namespace 258 259 SkDiscardableMemoryPool* SkDiscardableMemoryPool::Create(size_t size, SkBaseMutex* mutex) { 260 return SkNEW_ARGS(DiscardableMemoryPool, (size, mutex)); 261 } 262 263 SkDiscardableMemoryPool* SkGetGlobalDiscardableMemoryPool() { 264 SK_DECLARE_STATIC_LAZY_PTR(SkDiscardableMemoryPool, global, create_global_pool); 265 return global.get(); 266 } 267 268 //////////////////////////////////////////////////////////////////////////////// 269