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