| Index: src/lazy/SkDiscardableMemoryPool.cpp
|
| diff --git a/src/lazy/SkDiscardableMemoryPool.cpp b/src/lazy/SkDiscardableMemoryPool.cpp
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..a1b2438a2bcc79a9c80eb3c49c7afc281cda112b
|
| --- /dev/null
|
| +++ b/src/lazy/SkDiscardableMemoryPool.cpp
|
| @@ -0,0 +1,203 @@
|
| +/*
|
| + * 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 "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() {
|
| + // SkPoolDiscardableMemory objects that belong to this pool are
|
| + // always deleted before deleting this pool since each one has a
|
| + // ref to the 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 (fUsed <= budget) {
|
| + return;
|
| + }
|
| + typedef SkTInternalLList<SkPoolDiscardableMemory>::Iter Iter;
|
| + Iter iter;
|
| + SkPoolDiscardableMemory* cur = iter.init(fList, Iter::kTail_IterStart);
|
| + while ((fUsed > budget) && (NULL != cur)) {
|
| + if (!cur->fLocked) {
|
| + SkPoolDiscardableMemory* dm = cur;
|
| + SkASSERT(dm->fPointer != NULL);
|
| + sk_free(dm->fPointer);
|
| + 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) {
|
| + SkASSERT(dm != NULL);
|
| + 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) {
|
| + SkASSERT(dm != NULL);
|
| + 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;
|
| +}
|
| +
|
| +////////////////////////////////////////////////////////////////////////////////
|
|
|