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 "SkDiscardableMemoryPool.h" 9 #include "SkDiscardableMemory.h" 10 #include "SkMakeUnique.h" 11 #include "SkMalloc.h" 12 #include "SkMutex.h" 13 #include "SkTInternalLList.h" 14 #include "SkTemplates.h" 15 16 // Note: 17 // A PoolDiscardableMemory is memory that is counted in a pool. 18 // A DiscardableMemoryPool is a pool of PoolDiscardableMemorys. 19 20 namespace { 21 22 class PoolDiscardableMemory; 23 24 /** 25 * This non-global pool can be used for unit tests to verify that the 26 * pool works. 27 */ 28 class DiscardableMemoryPool : public SkDiscardableMemoryPool { 29 public: 30 DiscardableMemoryPool(size_t budget); 31 ~DiscardableMemoryPool() override; 32 33 std::unique_ptr<SkDiscardableMemory> make(size_t bytes); 34 SkDiscardableMemory* create(size_t bytes) override { 35 return this->make(bytes).release(); // TODO: change API 36 } 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 55 private: 56 SkMutex 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 removeFromPool(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 */ 79 class PoolDiscardableMemory : public SkDiscardableMemory { 80 public: 81 PoolDiscardableMemory(sk_sp<DiscardableMemoryPool> pool, SkAutoFree pointer, size_t bytes); 82 ~PoolDiscardableMemory() override; 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 sk_sp<DiscardableMemoryPool> fPool; 90 bool fLocked; 91 SkAutoFree fPointer; 92 const size_t fBytes; 93 }; 94 95 PoolDiscardableMemory::PoolDiscardableMemory(sk_sp<DiscardableMemoryPool> pool, 96 SkAutoFree pointer, 97 size_t bytes) 98 : fPool(std::move(pool)), fLocked(true), fPointer(std::move(pointer)), fBytes(bytes) { 99 SkASSERT(fPool != nullptr); 100 SkASSERT(fPointer != nullptr); 101 SkASSERT(fBytes > 0); 102 } 103 104 PoolDiscardableMemory::~PoolDiscardableMemory() { 105 SkASSERT(!fLocked); // contract for SkDiscardableMemory 106 fPool->removeFromPool(this); 107 } 108 109 bool PoolDiscardableMemory::lock() { 110 SkASSERT(!fLocked); // contract for SkDiscardableMemory 111 return fPool->lock(this); 112 } 113 114 void* PoolDiscardableMemory::data() { 115 SkASSERT(fLocked); // contract for SkDiscardableMemory 116 return fPointer.get(); 117 } 118 119 void PoolDiscardableMemory::unlock() { 120 SkASSERT(fLocked); // contract for SkDiscardableMemory 121 fPool->unlock(this); 122 } 123 124 //////////////////////////////////////////////////////////////////////////////// 125 126 DiscardableMemoryPool::DiscardableMemoryPool(size_t budget) 127 : fBudget(budget) 128 , fUsed(0) { 129 #if SK_LAZY_CACHE_STATS 130 fCacheHits = 0; 131 fCacheMisses = 0; 132 #endif // SK_LAZY_CACHE_STATS 133 } 134 DiscardableMemoryPool::~DiscardableMemoryPool() { 135 // PoolDiscardableMemory objects that belong to this pool are 136 // always deleted before deleting this pool since each one has a 137 // ref to the pool. 138 SkASSERT(fList.isEmpty()); 139 } 140 141 void DiscardableMemoryPool::dumpDownTo(size_t budget) { 142 fMutex.assertHeld(); 143 if (fUsed <= budget) { 144 return; 145 } 146 using Iter = SkTInternalLList<PoolDiscardableMemory>::Iter; 147 Iter iter; 148 PoolDiscardableMemory* cur = iter.init(fList, Iter::kTail_IterStart); 149 while ((fUsed > budget) && (cur)) { 150 if (!cur->fLocked) { 151 PoolDiscardableMemory* dm = cur; 152 SkASSERT(dm->fPointer != nullptr); 153 dm->fPointer = nullptr; 154 SkASSERT(fUsed >= dm->fBytes); 155 fUsed -= dm->fBytes; 156 cur = iter.prev(); 157 // Purged DMs are taken out of the list. This saves times 158 // looking them up. Purged DMs are NOT deleted. 159 fList.remove(dm); 160 } else { 161 cur = iter.prev(); 162 } 163 } 164 } 165 166 std::unique_ptr<SkDiscardableMemory> DiscardableMemoryPool::make(size_t bytes) { 167 SkAutoFree addr(sk_malloc_canfail(bytes)); 168 if (nullptr == addr) { 169 return nullptr; 170 } 171 auto dm = skstd::make_unique<PoolDiscardableMemory>(sk_ref_sp(this), std::move(addr), bytes); 172 SkAutoMutexAcquire autoMutexAcquire(fMutex); 173 fList.addToHead(dm.get()); 174 fUsed += bytes; 175 this->dumpDownTo(fBudget); 176 return std::move(dm); 177 } 178 179 void DiscardableMemoryPool::removeFromPool(PoolDiscardableMemory* dm) { 180 SkAutoMutexAcquire autoMutexAcquire(fMutex); 181 // This is called by dm's destructor. 182 if (dm->fPointer != nullptr) { 183 SkASSERT(fUsed >= dm->fBytes); 184 fUsed -= dm->fBytes; 185 fList.remove(dm); 186 } else { 187 SkASSERT(!fList.isInList(dm)); 188 } 189 } 190 191 bool DiscardableMemoryPool::lock(PoolDiscardableMemory* dm) { 192 SkASSERT(dm != nullptr); 193 SkAutoMutexAcquire autoMutexAcquire(fMutex); 194 if (nullptr == dm->fPointer) { 195 // May have been purged while waiting for lock. 196 #if SK_LAZY_CACHE_STATS 197 ++fCacheMisses; 198 #endif // SK_LAZY_CACHE_STATS 199 return false; 200 } 201 dm->fLocked = true; 202 fList.remove(dm); 203 fList.addToHead(dm); 204 #if SK_LAZY_CACHE_STATS 205 ++fCacheHits; 206 #endif // SK_LAZY_CACHE_STATS 207 return true; 208 } 209 210 void DiscardableMemoryPool::unlock(PoolDiscardableMemory* dm) { 211 SkASSERT(dm != nullptr); 212 SkAutoMutexAcquire autoMutexAcquire(fMutex); 213 dm->fLocked = false; 214 this->dumpDownTo(fBudget); 215 } 216 217 size_t DiscardableMemoryPool::getRAMUsed() { 218 return fUsed; 219 } 220 void DiscardableMemoryPool::setRAMBudget(size_t budget) { 221 SkAutoMutexAcquire autoMutexAcquire(fMutex); 222 fBudget = budget; 223 this->dumpDownTo(fBudget); 224 } 225 void DiscardableMemoryPool::dumpPool() { 226 SkAutoMutexAcquire autoMutexAcquire(fMutex); 227 this->dumpDownTo(0); 228 } 229 230 } // namespace 231 232 sk_sp<SkDiscardableMemoryPool> SkDiscardableMemoryPool::Make(size_t size) { 233 return sk_make_sp<DiscardableMemoryPool>(size); 234 } 235 236 SkDiscardableMemoryPool* SkGetGlobalDiscardableMemoryPool() { 237 // Intentionally leak this global pool. 238 static SkDiscardableMemoryPool* global = 239 new DiscardableMemoryPool(SK_DEFAULT_GLOBAL_DISCARDABLE_MEMORY_POOL_SIZE); 240 return global; 241 } 242