Chromium Code Reviews| Index: src/lazy/SkDiscardableMemoryPool.cpp |
| diff --git a/src/lazy/SkDiscardableMemoryPool.cpp b/src/lazy/SkDiscardableMemoryPool.cpp |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..d8edaec6f48746b880dbc0ceb40574e1f3a9dc79 |
| --- /dev/null |
| +++ b/src/lazy/SkDiscardableMemoryPool.cpp |
| @@ -0,0 +1,202 @@ |
| +/* |
| + * Copyright 2013 Google Inc. |
| + * |
| + * Use of this source code is governed by a BSD-style license that can be |
| + * found in the LICENSE file. |
| + */ |
| + |
| +#include "SkDiscardableMemoryPool.h" |
| +#include "SkThread.h" |
| +#include "SkOnce.h" |
| + |
| +// Note: |
| +// A PoolDiscardableMemory is memory that is counted in a pool. |
| +// A DiscardableMemoryPool is a pool of PoolDiscardableMemorys. |
| + |
| +/** |
| + * A SkPoolDiscardableMemory is a SkDiscardableMemory that relies on |
| + * a SkDiscardableMemoryPool object to manage the memory. |
| + */ |
| +class SkPoolDiscardableMemory : public SkDiscardableMemory { |
| +public: |
| + SkPoolDiscardableMemory(SkDiscardableMemoryPool* pool, |
| + void* pointer, size_t bytes); |
| + virtual ~SkPoolDiscardableMemory(); |
| + virtual bool lock() SK_OVERRIDE; |
| + virtual void* data() SK_OVERRIDE; |
| + virtual void unlock() SK_OVERRIDE; |
| + friend class SkDiscardableMemoryPool; |
| +private: |
| + SK_DECLARE_INTERNAL_LLIST_INTERFACE(SkPoolDiscardableMemory); |
| + SkDiscardableMemoryPool* const fPool; |
| + bool fLocked; |
| + void* fPointer; |
| + const size_t fBytes; |
| +}; |
| + |
| +SkPoolDiscardableMemory::SkPoolDiscardableMemory(SkDiscardableMemoryPool* pool, |
| + void* pointer, |
| + size_t bytes) |
| + : fPool(pool) |
| + , fLocked(true) |
| + , fPointer(pointer) |
| + , fBytes(bytes) { |
| + SkASSERT(fPool != NULL); |
| + SkASSERT(fPointer != NULL); |
| + SkASSERT(fBytes > 0); |
| + fPool->ref(); |
| +} |
| + |
| +SkPoolDiscardableMemory::~SkPoolDiscardableMemory() { |
| + fPool->free(this); |
| + fPool->unref(); |
| +} |
| + |
| +bool SkPoolDiscardableMemory::lock() { |
| + return fPool->lock(this); |
| +} |
| + |
| +void* SkPoolDiscardableMemory::data() { |
| + return fLocked ? fPointer : NULL; |
| +} |
| + |
| +void SkPoolDiscardableMemory::unlock() { |
| + fPool->unlock(this); |
| +} |
| + |
| +//////////////////////////////////////////////////////////////////////////////// |
| + |
| +SkDiscardableMemoryPool::SkDiscardableMemoryPool(size_t budget, |
| + SkBaseMutex* mutex) |
| + : fMutex(mutex) |
| + , fBudget(budget) |
| + , fUsed(0) { |
| + #if LAZY_CACHE_STATS |
| + fCacheHits = 0; |
| + fCacheMisses = 0; |
| + #endif // LAZY_CACHE_STATS |
| +} |
| +SkDiscardableMemoryPool::~SkDiscardableMemoryPool() { |
| + // Please delete the SkPoolDiscardableMemory objects that belong |
| + // to this pool before deleting this pool. |
| + SkASSERT(fList.isEmpty()); |
| +} |
| + |
| +void SkDiscardableMemoryPool::dumpDownTo(size_t budget) { |
| + // assert((NULL = fMutex) || fMutex->isLocked()); |
| + // TODO(halcanary) implement bool fMutex::isLocked(). |
| + // WARNING: only call this function after aquiring lock. |
| + if (budget <= fUsed) { |
| + return; |
| + } |
| + typedef SkTInternalLList<SkPoolDiscardableMemory>::Iter Iter; |
| + Iter iter; |
| + SkPoolDiscardableMemory* cur = iter.init(fList, Iter::kTail_IterStart); |
| + while ((budget > fUsed) && (NULL != cur)) { |
| + 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.
|
| + SkPoolDiscardableMemory* dm = cur; |
| + SkASSERT(dm->fPointer != NULL); |
| + 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
|
| + dm->fPointer = NULL; |
| + SkASSERT(fUsed >= dm->fBytes); |
| + fUsed -= dm->fBytes; |
| + cur = iter.prev(); |
| + // Purged DMs are taken out of the list. This saves times |
| + // looking them up. Purged DMs are NOT deleted. |
| + fList.remove(dm); |
| + } else { |
| + cur = iter.prev(); |
| + } |
| + } |
| +} |
| + |
| +SkDiscardableMemory* SkDiscardableMemoryPool::create(size_t bytes) { |
| + void* addr = sk_malloc_flags(bytes, 0); |
| + if (NULL == addr) { |
| + return NULL; |
| + } |
| + SkPoolDiscardableMemory* dm = SkNEW_ARGS(SkPoolDiscardableMemory, |
| + (this, addr, bytes)); |
| + SkAutoMutexAcquire autoMutexAcquire(fMutex); |
| + fList.addToHead(dm); |
| + fUsed += bytes; |
| + this->dumpDownTo(fBudget); |
| + return dm; |
| +} |
| + |
| +void SkDiscardableMemoryPool::free(SkPoolDiscardableMemory* dm) { |
| + // This is called by dm's destructor. |
| + if (dm->fPointer != NULL) { |
| + SkAutoMutexAcquire autoMutexAcquire(fMutex); |
| + sk_free(dm->fPointer); |
| + dm->fPointer = NULL; |
| + SkASSERT(fUsed >= dm->fBytes); |
| + fUsed -= dm->fBytes; |
| + fList.remove(dm); |
| + } else { |
| + SkASSERT(!fList.isInList(dm)); |
| + } |
| + |
| +} |
| + |
| +bool SkDiscardableMemoryPool::lock(SkPoolDiscardableMemory* dm) { |
| + if (NULL == dm->fPointer) { |
| + #if LAZY_CACHE_STATS |
| + SkAutoMutexAcquire autoMutexAcquire(fMutex); |
| + ++fCacheMisses; |
| + #endif // LAZY_CACHE_STATS |
| + return false; |
| + } |
| + SkAutoMutexAcquire autoMutexAcquire(fMutex); |
| + if (NULL == dm->fPointer) { |
| + // May have been purged while waiting for lock. |
| + #if LAZY_CACHE_STATS |
| + ++fCacheMisses; |
| + #endif // LAZY_CACHE_STATS |
| + return false; |
| + } |
| + dm->fLocked = true; |
| + fList.remove(dm); |
| + fList.addToHead(dm); |
| + #if LAZY_CACHE_STATS |
| + ++fCacheHits; |
| + #endif // LAZY_CACHE_STATS |
| + return true; |
| +} |
| + |
| +void SkDiscardableMemoryPool::unlock(SkPoolDiscardableMemory* dm) { |
| + SkAutoMutexAcquire autoMutexAcquire(fMutex); |
| + dm->fLocked = false; |
| + this->dumpDownTo(fBudget); |
| +} |
| + |
| +size_t SkDiscardableMemoryPool::getRAMUsed() { |
| + return fUsed; |
| +} |
| +void SkDiscardableMemoryPool::setRAMBudget(size_t budget) { |
| + SkAutoMutexAcquire autoMutexAcquire(fMutex); |
| + fBudget = budget; |
| + this->dumpDownTo(fBudget); |
| +} |
| +void SkDiscardableMemoryPool::dumpPool() { |
| + SkAutoMutexAcquire autoMutexAcquire(fMutex); |
| + this->dumpDownTo(0); |
| +} |
| + |
| +//////////////////////////////////////////////////////////////////////////////// |
| +SK_DECLARE_STATIC_MUTEX(gMutex); |
| +static void create_pool(SkDiscardableMemoryPool** pool) { |
| + SkASSERT(NULL == *pool); |
| + *pool = SkNEW_ARGS(SkDiscardableMemoryPool, |
| + (SK_DEFAULT_GLOBAL_DISCARDABLE_MEMORY_POOL_SIZE, |
| + &gMutex)); |
| +} |
| +SkDiscardableMemoryPool* SkGetGlobalDiscardableMemoryPool() { |
| + static SkDiscardableMemoryPool* gPool(NULL); |
| + SK_DECLARE_STATIC_ONCE(create_pool_once); |
| + SkOnce(&create_pool_once, create_pool, &gPool); |
| + SkASSERT(NULL != gPool); |
| + return gPool; |
| +} |
| + |
| +//////////////////////////////////////////////////////////////////////////////// |