| /* |
| * 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() { |
| SkASSERT(!fLocked); // contract for SkDiscardableMemory |
| fPool->free(this); |
| fPool->unref(); |
| } |
| |
| bool SkPoolDiscardableMemory::lock() { |
| SkASSERT(!fLocked); // contract for SkDiscardableMemory |
| return fPool->lock(this); |
| } |
| |
| void* SkPoolDiscardableMemory::data() { |
| SkASSERT(fLocked); // contract for SkDiscardableMemory |
| return fPointer; |
| } |
| |
| void SkPoolDiscardableMemory::unlock() { |
| SkASSERT(fLocked); // contract for SkDiscardableMemory |
| 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; |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |