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