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 "SkMutex.h" 12 #include "SkOncePtr.h" 13 #include "SkTInternalLList.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 = nullptr); 33 virtual ~DiscardableMemoryPool(); 34 35 SkDiscardableMemory* create(size_t bytes) override; 36 37 size_t getRAMUsed() override; 38 void setRAMBudget(size_t budget) override; 39 size_t getRAMBudget() override { return fBudget; } 40 41 /** purges all unlocked DMs */ 42 void dumpPool() override; 43 44 #if SK_LAZY_CACHE_STATS // Defined in SkDiscardableMemoryPool.h 45 int getCacheHits() override { return fCacheHits; } 46 int getCacheMisses() override { return fCacheMisses; } 47 void resetCacheHitsAndMisses() 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 bool lock() override; 84 void* data() override; 85 void unlock() 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 != nullptr); 103 SkASSERT(fPointer != nullptr); 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 != nullptr) { 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 != nullptr); 162 sk_free(dm->fPointer); 163 dm->fPointer = nullptr; 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 (nullptr == addr) { 179 return nullptr; 180 } 181 PoolDiscardableMemory* dm = new PoolDiscardableMemory(this, addr, bytes); 182 SkAutoMutexAcquire autoMutexAcquire(fMutex); 183 fList.addToHead(dm); 184 fUsed += bytes; 185 this->dumpDownTo(fBudget); 186 return dm; 187 } 188 189 void DiscardableMemoryPool::free(PoolDiscardableMemory* dm) { 190 SkAutoMutexAcquire autoMutexAcquire(fMutex); 191 // This is called by dm's destructor. 192 if (dm->fPointer != nullptr) { 193 sk_free(dm->fPointer); 194 dm->fPointer = nullptr; 195 SkASSERT(fUsed >= dm->fBytes); 196 fUsed -= dm->fBytes; 197 fList.remove(dm); 198 } else { 199 SkASSERT(!fList.isInList(dm)); 200 } 201 } 202 203 bool DiscardableMemoryPool::lock(PoolDiscardableMemory* dm) { 204 SkASSERT(dm != nullptr); 205 SkAutoMutexAcquire autoMutexAcquire(fMutex); 206 if (nullptr == dm->fPointer) { 207 // May have been purged while waiting for lock. 208 #if SK_LAZY_CACHE_STATS 209 ++fCacheMisses; 210 #endif // SK_LAZY_CACHE_STATS 211 return false; 212 } 213 dm->fLocked = true; 214 fList.remove(dm); 215 fList.addToHead(dm); 216 #if SK_LAZY_CACHE_STATS 217 ++fCacheHits; 218 #endif // SK_LAZY_CACHE_STATS 219 return true; 220 } 221 222 void DiscardableMemoryPool::unlock(PoolDiscardableMemory* dm) { 223 SkASSERT(dm != nullptr); 224 SkAutoMutexAcquire autoMutexAcquire(fMutex); 225 dm->fLocked = false; 226 this->dumpDownTo(fBudget); 227 } 228 229 size_t DiscardableMemoryPool::getRAMUsed() { 230 return fUsed; 231 } 232 void DiscardableMemoryPool::setRAMBudget(size_t budget) { 233 SkAutoMutexAcquire autoMutexAcquire(fMutex); 234 fBudget = budget; 235 this->dumpDownTo(fBudget); 236 } 237 void DiscardableMemoryPool::dumpPool() { 238 SkAutoMutexAcquire autoMutexAcquire(fMutex); 239 this->dumpDownTo(0); 240 } 241 242 } // namespace 243 244 SkDiscardableMemoryPool* SkDiscardableMemoryPool::Create(size_t size, SkBaseMutex* mutex) { 245 return new DiscardableMemoryPool(size, mutex); 246 } 247 248 SK_DECLARE_STATIC_MUTEX(gMutex); 249 SK_DECLARE_STATIC_ONCE_PTR(SkDiscardableMemoryPool, global); 250 251 SkDiscardableMemoryPool* SkGetGlobalDiscardableMemoryPool() { 252 return global.get([] { 253 return SkDiscardableMemoryPool::Create(SK_DEFAULT_GLOBAL_DISCARDABLE_MEMORY_POOL_SIZE, 254 &gMutex); 255 }); 256 } 257