OLD | NEW |
(Empty) | |
| 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 "SkOnce.h" |
| 10 #include "SkThread.h" |
| 11 |
| 12 // Note: |
| 13 // A PoolDiscardableMemory is memory that is counted in a pool. |
| 14 // A DiscardableMemoryPool is a pool of PoolDiscardableMemorys. |
| 15 |
| 16 /** |
| 17 * A SkPoolDiscardableMemory is a SkDiscardableMemory that relies on |
| 18 * a SkDiscardableMemoryPool object to manage the memory. |
| 19 */ |
| 20 class SkPoolDiscardableMemory : public SkDiscardableMemory { |
| 21 public: |
| 22 SkPoolDiscardableMemory(SkDiscardableMemoryPool* pool, |
| 23 void* pointer, size_t bytes); |
| 24 virtual ~SkPoolDiscardableMemory(); |
| 25 virtual bool lock() SK_OVERRIDE; |
| 26 virtual void* data() SK_OVERRIDE; |
| 27 virtual void unlock() SK_OVERRIDE; |
| 28 friend class SkDiscardableMemoryPool; |
| 29 private: |
| 30 SK_DECLARE_INTERNAL_LLIST_INTERFACE(SkPoolDiscardableMemory); |
| 31 SkDiscardableMemoryPool* const fPool; |
| 32 bool fLocked; |
| 33 void* fPointer; |
| 34 const size_t fBytes; |
| 35 }; |
| 36 |
| 37 SkPoolDiscardableMemory::SkPoolDiscardableMemory(SkDiscardableMemoryPool* pool, |
| 38 void* pointer, |
| 39 size_t bytes) |
| 40 : fPool(pool) |
| 41 , fLocked(true) |
| 42 , fPointer(pointer) |
| 43 , fBytes(bytes) { |
| 44 SkASSERT(fPool != NULL); |
| 45 SkASSERT(fPointer != NULL); |
| 46 SkASSERT(fBytes > 0); |
| 47 fPool->ref(); |
| 48 } |
| 49 |
| 50 SkPoolDiscardableMemory::~SkPoolDiscardableMemory() { |
| 51 fPool->free(this); |
| 52 fPool->unref(); |
| 53 } |
| 54 |
| 55 bool SkPoolDiscardableMemory::lock() { |
| 56 return fPool->lock(this); |
| 57 } |
| 58 |
| 59 void* SkPoolDiscardableMemory::data() { |
| 60 return fLocked ? fPointer : NULL; |
| 61 } |
| 62 |
| 63 void SkPoolDiscardableMemory::unlock() { |
| 64 fPool->unlock(this); |
| 65 } |
| 66 |
| 67 //////////////////////////////////////////////////////////////////////////////// |
| 68 |
| 69 SkDiscardableMemoryPool::SkDiscardableMemoryPool(size_t budget, |
| 70 SkBaseMutex* mutex) |
| 71 : fMutex(mutex) |
| 72 , fBudget(budget) |
| 73 , fUsed(0) { |
| 74 #if LAZY_CACHE_STATS |
| 75 fCacheHits = 0; |
| 76 fCacheMisses = 0; |
| 77 #endif // LAZY_CACHE_STATS |
| 78 } |
| 79 SkDiscardableMemoryPool::~SkDiscardableMemoryPool() { |
| 80 // SkPoolDiscardableMemory objects that belong to this pool are |
| 81 // always deleted before deleting this pool since each one has a |
| 82 // ref to the pool. |
| 83 SkASSERT(fList.isEmpty()); |
| 84 } |
| 85 |
| 86 void SkDiscardableMemoryPool::dumpDownTo(size_t budget) { |
| 87 // assert((NULL = fMutex) || fMutex->isLocked()); |
| 88 // TODO(halcanary) implement bool fMutex::isLocked(). |
| 89 // WARNING: only call this function after aquiring lock. |
| 90 if (fUsed <= budget) { |
| 91 return; |
| 92 } |
| 93 typedef SkTInternalLList<SkPoolDiscardableMemory>::Iter Iter; |
| 94 Iter iter; |
| 95 SkPoolDiscardableMemory* cur = iter.init(fList, Iter::kTail_IterStart); |
| 96 while ((fUsed > budget) && (NULL != cur)) { |
| 97 if (!cur->fLocked) { |
| 98 SkPoolDiscardableMemory* dm = cur; |
| 99 SkASSERT(dm->fPointer != NULL); |
| 100 sk_free(dm->fPointer); |
| 101 dm->fPointer = NULL; |
| 102 SkASSERT(fUsed >= dm->fBytes); |
| 103 fUsed -= dm->fBytes; |
| 104 cur = iter.prev(); |
| 105 // Purged DMs are taken out of the list. This saves times |
| 106 // looking them up. Purged DMs are NOT deleted. |
| 107 fList.remove(dm); |
| 108 } else { |
| 109 cur = iter.prev(); |
| 110 } |
| 111 } |
| 112 } |
| 113 |
| 114 SkDiscardableMemory* SkDiscardableMemoryPool::create(size_t bytes) { |
| 115 void* addr = sk_malloc_flags(bytes, 0); |
| 116 if (NULL == addr) { |
| 117 return NULL; |
| 118 } |
| 119 SkPoolDiscardableMemory* dm = SkNEW_ARGS(SkPoolDiscardableMemory, |
| 120 (this, addr, bytes)); |
| 121 SkAutoMutexAcquire autoMutexAcquire(fMutex); |
| 122 fList.addToHead(dm); |
| 123 fUsed += bytes; |
| 124 this->dumpDownTo(fBudget); |
| 125 return dm; |
| 126 } |
| 127 |
| 128 void SkDiscardableMemoryPool::free(SkPoolDiscardableMemory* dm) { |
| 129 // This is called by dm's destructor. |
| 130 if (dm->fPointer != NULL) { |
| 131 SkAutoMutexAcquire autoMutexAcquire(fMutex); |
| 132 sk_free(dm->fPointer); |
| 133 dm->fPointer = NULL; |
| 134 SkASSERT(fUsed >= dm->fBytes); |
| 135 fUsed -= dm->fBytes; |
| 136 fList.remove(dm); |
| 137 } else { |
| 138 SkASSERT(!fList.isInList(dm)); |
| 139 } |
| 140 } |
| 141 |
| 142 bool SkDiscardableMemoryPool::lock(SkPoolDiscardableMemory* dm) { |
| 143 SkASSERT(dm != NULL); |
| 144 if (NULL == dm->fPointer) { |
| 145 #if LAZY_CACHE_STATS |
| 146 SkAutoMutexAcquire autoMutexAcquire(fMutex); |
| 147 ++fCacheMisses; |
| 148 #endif // LAZY_CACHE_STATS |
| 149 return false; |
| 150 } |
| 151 SkAutoMutexAcquire autoMutexAcquire(fMutex); |
| 152 if (NULL == dm->fPointer) { |
| 153 // May have been purged while waiting for lock. |
| 154 #if LAZY_CACHE_STATS |
| 155 ++fCacheMisses; |
| 156 #endif // LAZY_CACHE_STATS |
| 157 return false; |
| 158 } |
| 159 dm->fLocked = true; |
| 160 fList.remove(dm); |
| 161 fList.addToHead(dm); |
| 162 #if LAZY_CACHE_STATS |
| 163 ++fCacheHits; |
| 164 #endif // LAZY_CACHE_STATS |
| 165 return true; |
| 166 } |
| 167 |
| 168 void SkDiscardableMemoryPool::unlock(SkPoolDiscardableMemory* dm) { |
| 169 SkASSERT(dm != NULL); |
| 170 SkAutoMutexAcquire autoMutexAcquire(fMutex); |
| 171 dm->fLocked = false; |
| 172 this->dumpDownTo(fBudget); |
| 173 } |
| 174 |
| 175 size_t SkDiscardableMemoryPool::getRAMUsed() { |
| 176 return fUsed; |
| 177 } |
| 178 void SkDiscardableMemoryPool::setRAMBudget(size_t budget) { |
| 179 SkAutoMutexAcquire autoMutexAcquire(fMutex); |
| 180 fBudget = budget; |
| 181 this->dumpDownTo(fBudget); |
| 182 } |
| 183 void SkDiscardableMemoryPool::dumpPool() { |
| 184 SkAutoMutexAcquire autoMutexAcquire(fMutex); |
| 185 this->dumpDownTo(0); |
| 186 } |
| 187 |
| 188 //////////////////////////////////////////////////////////////////////////////// |
| 189 SK_DECLARE_STATIC_MUTEX(gMutex); |
| 190 static void create_pool(SkDiscardableMemoryPool** pool) { |
| 191 SkASSERT(NULL == *pool); |
| 192 *pool = SkNEW_ARGS(SkDiscardableMemoryPool, |
| 193 (SK_DEFAULT_GLOBAL_DISCARDABLE_MEMORY_POOL_SIZE, |
| 194 &gMutex)); |
| 195 } |
| 196 SkDiscardableMemoryPool* SkGetGlobalDiscardableMemoryPool() { |
| 197 static SkDiscardableMemoryPool* gPool(NULL); |
| 198 SK_DECLARE_STATIC_ONCE(create_pool_once); |
| 199 SkOnce(&create_pool_once, create_pool, &gPool); |
| 200 SkASSERT(NULL != gPool); |
| 201 return gPool; |
| 202 } |
| 203 |
| 204 //////////////////////////////////////////////////////////////////////////////// |
OLD | NEW |