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 "SkMalloc.h" 12#include "SkMutex.h" 13#include "SkOnce.h" 14#include "SkTInternalLList.h" 15 16// Note: 17// A PoolDiscardableMemory is memory that is counted in a pool. 18// A DiscardableMemoryPool is a pool of PoolDiscardableMemorys. 19 20namespace { 21 22class PoolDiscardableMemory; 23 24/** 25 * This non-global pool can be used for unit tests to verify that the 26 * pool works. 27 */ 28class DiscardableMemoryPool : public SkDiscardableMemoryPool { 29public: 30 /** 31 * Without mutex, will be not be thread safe. 32 */ 33 DiscardableMemoryPool(size_t budget, SkBaseMutex* mutex = nullptr); 34 ~DiscardableMemoryPool() override; 35 36 SkDiscardableMemory* create(size_t bytes) override; 37 38 size_t getRAMUsed() override; 39 void setRAMBudget(size_t budget) override; 40 size_t getRAMBudget() override { return fBudget; } 41 42 /** purges all unlocked DMs */ 43 void dumpPool() override; 44 45 #if SK_LAZY_CACHE_STATS // Defined in SkDiscardableMemoryPool.h 46 int getCacheHits() override { return fCacheHits; } 47 int getCacheMisses() override { return fCacheMisses; } 48 void resetCacheHitsAndMisses() override { 49 fCacheHits = fCacheMisses = 0; 50 } 51 int fCacheHits; 52 int fCacheMisses; 53 #endif // SK_LAZY_CACHE_STATS 54 55private: 56 SkBaseMutex* fMutex; 57 size_t fBudget; 58 size_t fUsed; 59 SkTInternalLList<PoolDiscardableMemory> fList; 60 61 /** Function called to free memory if needed */ 62 void dumpDownTo(size_t budget); 63 /** called by DiscardableMemoryPool upon destruction */ 64 void free(PoolDiscardableMemory* dm); 65 /** called by DiscardableMemoryPool::lock() */ 66 bool lock(PoolDiscardableMemory* dm); 67 /** called by DiscardableMemoryPool::unlock() */ 68 void unlock(PoolDiscardableMemory* dm); 69 70 friend class PoolDiscardableMemory; 71 72 typedef SkDiscardableMemory::Factory INHERITED; 73}; 74 75/** 76 * A PoolDiscardableMemory is a SkDiscardableMemory that relies on 77 * a DiscardableMemoryPool object to manage the memory. 78 */ 79class PoolDiscardableMemory : public SkDiscardableMemory { 80public: 81 PoolDiscardableMemory(DiscardableMemoryPool* pool, 82 void* pointer, size_t bytes); 83 ~PoolDiscardableMemory() override; 84 bool lock() override; 85 void* data() override; 86 void unlock() override; 87 friend class DiscardableMemoryPool; 88private: 89 SK_DECLARE_INTERNAL_LLIST_INTERFACE(PoolDiscardableMemory); 90 DiscardableMemoryPool* const fPool; 91 bool fLocked; 92 void* fPointer; 93 const size_t fBytes; 94}; 95 96PoolDiscardableMemory::PoolDiscardableMemory(DiscardableMemoryPool* pool, 97 void* pointer, 98 size_t bytes) 99 : fPool(pool) 100 , fLocked(true) 101 , fPointer(pointer) 102 , fBytes(bytes) { 103 SkASSERT(fPool != nullptr); 104 SkASSERT(fPointer != nullptr); 105 SkASSERT(fBytes > 0); 106 fPool->ref(); 107} 108 109PoolDiscardableMemory::~PoolDiscardableMemory() { 110 SkASSERT(!fLocked); // contract for SkDiscardableMemory 111 fPool->free(this); 112 fPool->unref(); 113} 114 115bool PoolDiscardableMemory::lock() { 116 SkASSERT(!fLocked); // contract for SkDiscardableMemory 117 return fPool->lock(this); 118} 119 120void* PoolDiscardableMemory::data() { 121 SkASSERT(fLocked); // contract for SkDiscardableMemory 122 return fPointer; 123} 124 125void PoolDiscardableMemory::unlock() { 126 SkASSERT(fLocked); // contract for SkDiscardableMemory 127 fPool->unlock(this); 128} 129 130//////////////////////////////////////////////////////////////////////////////// 131 132DiscardableMemoryPool::DiscardableMemoryPool(size_t budget, 133 SkBaseMutex* mutex) 134 : fMutex(mutex) 135 , fBudget(budget) 136 , fUsed(0) { 137 #if SK_LAZY_CACHE_STATS 138 fCacheHits = 0; 139 fCacheMisses = 0; 140 #endif // SK_LAZY_CACHE_STATS 141} 142DiscardableMemoryPool::~DiscardableMemoryPool() { 143 // PoolDiscardableMemory objects that belong to this pool are 144 // always deleted before deleting this pool since each one has a 145 // ref to the pool. 146 SkASSERT(fList.isEmpty()); 147} 148 149void DiscardableMemoryPool::dumpDownTo(size_t budget) { 150 if (fMutex != nullptr) { 151 fMutex->assertHeld(); 152 } 153 if (fUsed <= budget) { 154 return; 155 } 156 typedef SkTInternalLList<PoolDiscardableMemory>::Iter Iter; 157 Iter iter; 158 PoolDiscardableMemory* cur = iter.init(fList, Iter::kTail_IterStart); 159 while ((fUsed > budget) && (cur)) { 160 if (!cur->fLocked) { 161 PoolDiscardableMemory* dm = cur; 162 SkASSERT(dm->fPointer != nullptr); 163 sk_free(dm->fPointer); 164 dm->fPointer = nullptr; 165 SkASSERT(fUsed >= dm->fBytes); 166 fUsed -= dm->fBytes; 167 cur = iter.prev(); 168 // Purged DMs are taken out of the list. This saves times 169 // looking them up. Purged DMs are NOT deleted. 170 fList.remove(dm); 171 } else { 172 cur = iter.prev(); 173 } 174 } 175} 176 177SkDiscardableMemory* DiscardableMemoryPool::create(size_t bytes) { 178 void* addr = sk_malloc_flags(bytes, 0); 179 if (nullptr == addr) { 180 return nullptr; 181 } 182 PoolDiscardableMemory* dm = new PoolDiscardableMemory(this, addr, bytes); 183 SkAutoMutexAcquire autoMutexAcquire(fMutex); 184 fList.addToHead(dm); 185 fUsed += bytes; 186 this->dumpDownTo(fBudget); 187 return dm; 188} 189 190void DiscardableMemoryPool::free(PoolDiscardableMemory* dm) { 191 SkAutoMutexAcquire autoMutexAcquire(fMutex); 192 // This is called by dm's destructor. 193 if (dm->fPointer != nullptr) { 194 sk_free(dm->fPointer); 195 dm->fPointer = nullptr; 196 SkASSERT(fUsed >= dm->fBytes); 197 fUsed -= dm->fBytes; 198 fList.remove(dm); 199 } else { 200 SkASSERT(!fList.isInList(dm)); 201 } 202} 203 204bool DiscardableMemoryPool::lock(PoolDiscardableMemory* dm) { 205 SkASSERT(dm != nullptr); 206 SkAutoMutexAcquire autoMutexAcquire(fMutex); 207 if (nullptr == dm->fPointer) { 208 // May have been purged while waiting for lock. 209 #if SK_LAZY_CACHE_STATS 210 ++fCacheMisses; 211 #endif // SK_LAZY_CACHE_STATS 212 return false; 213 } 214 dm->fLocked = true; 215 fList.remove(dm); 216 fList.addToHead(dm); 217 #if SK_LAZY_CACHE_STATS 218 ++fCacheHits; 219 #endif // SK_LAZY_CACHE_STATS 220 return true; 221} 222 223void DiscardableMemoryPool::unlock(PoolDiscardableMemory* dm) { 224 SkASSERT(dm != nullptr); 225 SkAutoMutexAcquire autoMutexAcquire(fMutex); 226 dm->fLocked = false; 227 this->dumpDownTo(fBudget); 228} 229 230size_t DiscardableMemoryPool::getRAMUsed() { 231 return fUsed; 232} 233void DiscardableMemoryPool::setRAMBudget(size_t budget) { 234 SkAutoMutexAcquire autoMutexAcquire(fMutex); 235 fBudget = budget; 236 this->dumpDownTo(fBudget); 237} 238void DiscardableMemoryPool::dumpPool() { 239 SkAutoMutexAcquire autoMutexAcquire(fMutex); 240 this->dumpDownTo(0); 241} 242 243} // namespace 244 245SkDiscardableMemoryPool* SkDiscardableMemoryPool::Create(size_t size, SkBaseMutex* mutex) { 246 return new DiscardableMemoryPool(size, mutex); 247} 248 249SK_DECLARE_STATIC_MUTEX(gMutex); 250 251SkDiscardableMemoryPool* SkGetGlobalDiscardableMemoryPool() { 252 static SkOnce once; 253 static SkDiscardableMemoryPool* global; 254 once([]{ 255 global = SkDiscardableMemoryPool::Create(SK_DEFAULT_GLOBAL_DISCARDABLE_MEMORY_POOL_SIZE, 256 &gMutex); 257 }); 258 return global; 259} 260