Chromium Code Reviews| 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 "SkThread.h" | |
| 10 #include "SkOnce.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 // Please delete the SkPoolDiscardableMemory objects that belong | |
| 81 // to this pool before deleting this 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 (budget <= fUsed) { | |
| 90 return; | |
| 91 } | |
| 92 typedef SkTInternalLList<SkPoolDiscardableMemory>::Iter Iter; | |
| 93 Iter iter; | |
| 94 SkPoolDiscardableMemory* cur = iter.init(fList, Iter::kTail_IterStart); | |
| 95 while ((budget > fUsed) && (NULL != cur)) { | |
| 96 if ((!cur->fLocked) && (cur->fPointer != NULL)) { | |
|
scroggo
2013/12/03 23:00:01
Will curr->fPointer ever be NULL? I would think no
hal.canary
2013/12/04 00:01:36
Done.
| |
| 97 SkPoolDiscardableMemory* dm = cur; | |
| 98 SkASSERT(dm->fPointer != NULL); | |
| 99 sk_free(dm->fPointer); | |
|
scroggo
2013/12/03 23:00:01
Should this function call this->free(dm)?
hal.canary
2013/12/04 00:01:36
No. We have the mutex, so we can't directly call
| |
| 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 | |
| 142 bool SkDiscardableMemoryPool::lock(SkPoolDiscardableMemory* dm) { | |
| 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 SkAutoMutexAcquire autoMutexAcquire(fMutex); | |
| 169 dm->fLocked = false; | |
| 170 this->dumpDownTo(fBudget); | |
| 171 } | |
| 172 | |
| 173 size_t SkDiscardableMemoryPool::getRAMUsed() { | |
| 174 return fUsed; | |
| 175 } | |
| 176 void SkDiscardableMemoryPool::setRAMBudget(size_t budget) { | |
| 177 SkAutoMutexAcquire autoMutexAcquire(fMutex); | |
| 178 fBudget = budget; | |
| 179 this->dumpDownTo(fBudget); | |
| 180 } | |
| 181 void SkDiscardableMemoryPool::dumpPool() { | |
| 182 SkAutoMutexAcquire autoMutexAcquire(fMutex); | |
| 183 this->dumpDownTo(0); | |
| 184 } | |
| 185 | |
| 186 //////////////////////////////////////////////////////////////////////////////// | |
| 187 SK_DECLARE_STATIC_MUTEX(gMutex); | |
| 188 static void create_pool(SkDiscardableMemoryPool** pool) { | |
| 189 SkASSERT(NULL == *pool); | |
| 190 *pool = SkNEW_ARGS(SkDiscardableMemoryPool, | |
| 191 (SK_DEFAULT_GLOBAL_DISCARDABLE_MEMORY_POOL_SIZE, | |
| 192 &gMutex)); | |
| 193 } | |
| 194 SkDiscardableMemoryPool* SkGetGlobalDiscardableMemoryPool() { | |
| 195 static SkDiscardableMemoryPool* gPool(NULL); | |
| 196 SK_DECLARE_STATIC_ONCE(create_pool_once); | |
| 197 SkOnce(&create_pool_once, create_pool, &gPool); | |
| 198 SkASSERT(NULL != gPool); | |
| 199 return gPool; | |
| 200 } | |
| 201 | |
| 202 //////////////////////////////////////////////////////////////////////////////// | |
| OLD | NEW |