Home | History | Annotate | Download | only in lazy
      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 "SkDiscardableMemory.h"
      9 #include "SkDiscardableMemoryPool.h"
     10 #include "SkLazyPtr.h"
     11 #include "SkTInternalLList.h"
     12 #include "SkThread.h"
     13 
     14 // Note:
     15 // A PoolDiscardableMemory is memory that is counted in a pool.
     16 // A DiscardableMemoryPool is a pool of PoolDiscardableMemorys.
     17 
     18 namespace {
     19 
     20 class PoolDiscardableMemory;
     21 
     22 /**
     23  *  This non-global pool can be used for unit tests to verify that the
     24  *  pool works.
     25  */
     26 class DiscardableMemoryPool : public SkDiscardableMemoryPool {
     27 public:
     28     /**
     29      *  Without mutex, will be not be thread safe.
     30      */
     31     DiscardableMemoryPool(size_t budget, SkBaseMutex* mutex = NULL);
     32     virtual ~DiscardableMemoryPool();
     33 
     34     virtual SkDiscardableMemory* create(size_t bytes) SK_OVERRIDE;
     35 
     36     virtual size_t getRAMUsed() SK_OVERRIDE;
     37     virtual void setRAMBudget(size_t budget) SK_OVERRIDE;
     38     virtual size_t getRAMBudget() SK_OVERRIDE { return fBudget; }
     39 
     40     /** purges all unlocked DMs */
     41     virtual void dumpPool() SK_OVERRIDE;
     42 
     43     #if SK_LAZY_CACHE_STATS  // Defined in SkDiscardableMemoryPool.h
     44     virtual int getCacheHits() SK_OVERRIDE { return fCacheHits; }
     45     virtual int getCacheMisses() SK_OVERRIDE { return fCacheMisses; }
     46     virtual void resetCacheHitsAndMisses() SK_OVERRIDE {
     47         fCacheHits = fCacheMisses = 0;
     48     }
     49     int          fCacheHits;
     50     int          fCacheMisses;
     51     #endif  // SK_LAZY_CACHE_STATS
     52 
     53 private:
     54     SkBaseMutex* fMutex;
     55     size_t       fBudget;
     56     size_t       fUsed;
     57     SkTInternalLList<PoolDiscardableMemory> fList;
     58 
     59     /** Function called to free memory if needed */
     60     void dumpDownTo(size_t budget);
     61     /** called by DiscardableMemoryPool upon destruction */
     62     void free(PoolDiscardableMemory* dm);
     63     /** called by DiscardableMemoryPool::lock() */
     64     bool lock(PoolDiscardableMemory* dm);
     65     /** called by DiscardableMemoryPool::unlock() */
     66     void unlock(PoolDiscardableMemory* dm);
     67 
     68     friend class PoolDiscardableMemory;
     69 
     70     typedef SkDiscardableMemory::Factory INHERITED;
     71 };
     72 
     73 /**
     74  *  A PoolDiscardableMemory is a SkDiscardableMemory that relies on
     75  *  a DiscardableMemoryPool object to manage the memory.
     76  */
     77 class PoolDiscardableMemory : public SkDiscardableMemory {
     78 public:
     79     PoolDiscardableMemory(DiscardableMemoryPool* pool,
     80                             void* pointer, size_t bytes);
     81     virtual ~PoolDiscardableMemory();
     82     virtual bool lock() SK_OVERRIDE;
     83     virtual void* data() SK_OVERRIDE;
     84     virtual void unlock() SK_OVERRIDE;
     85     friend class DiscardableMemoryPool;
     86 private:
     87     SK_DECLARE_INTERNAL_LLIST_INTERFACE(PoolDiscardableMemory);
     88     DiscardableMemoryPool* const fPool;
     89     bool                         fLocked;
     90     void*                        fPointer;
     91     const size_t                 fBytes;
     92 };
     93 
     94 PoolDiscardableMemory::PoolDiscardableMemory(DiscardableMemoryPool* pool,
     95                                              void* pointer,
     96                                              size_t bytes)
     97     : fPool(pool)
     98     , fLocked(true)
     99     , fPointer(pointer)
    100     , fBytes(bytes) {
    101     SkASSERT(fPool != NULL);
    102     SkASSERT(fPointer != NULL);
    103     SkASSERT(fBytes > 0);
    104     fPool->ref();
    105 }
    106 
    107 PoolDiscardableMemory::~PoolDiscardableMemory() {
    108     SkASSERT(!fLocked); // contract for SkDiscardableMemory
    109     fPool->free(this);
    110     fPool->unref();
    111 }
    112 
    113 bool PoolDiscardableMemory::lock() {
    114     SkASSERT(!fLocked); // contract for SkDiscardableMemory
    115     return fPool->lock(this);
    116 }
    117 
    118 void* PoolDiscardableMemory::data() {
    119     SkASSERT(fLocked); // contract for SkDiscardableMemory
    120     return fPointer;
    121 }
    122 
    123 void PoolDiscardableMemory::unlock() {
    124     SkASSERT(fLocked); // contract for SkDiscardableMemory
    125     fPool->unlock(this);
    126 }
    127 
    128 ////////////////////////////////////////////////////////////////////////////////
    129 
    130 DiscardableMemoryPool::DiscardableMemoryPool(size_t budget,
    131                                              SkBaseMutex* mutex)
    132     : fMutex(mutex)
    133     , fBudget(budget)
    134     , fUsed(0) {
    135     #if SK_LAZY_CACHE_STATS
    136     fCacheHits = 0;
    137     fCacheMisses = 0;
    138     #endif  // SK_LAZY_CACHE_STATS
    139 }
    140 DiscardableMemoryPool::~DiscardableMemoryPool() {
    141     // PoolDiscardableMemory objects that belong to this pool are
    142     // always deleted before deleting this pool since each one has a
    143     // ref to the pool.
    144     SkASSERT(fList.isEmpty());
    145 }
    146 
    147 void DiscardableMemoryPool::dumpDownTo(size_t budget) {
    148     if (fMutex != NULL) {
    149         fMutex->assertHeld();
    150     }
    151     if (fUsed <= budget) {
    152         return;
    153     }
    154     typedef SkTInternalLList<PoolDiscardableMemory>::Iter Iter;
    155     Iter iter;
    156     PoolDiscardableMemory* cur = iter.init(fList, Iter::kTail_IterStart);
    157     while ((fUsed > budget) && (NULL != cur)) {
    158         if (!cur->fLocked) {
    159             PoolDiscardableMemory* dm = cur;
    160             SkASSERT(dm->fPointer != NULL);
    161             sk_free(dm->fPointer);
    162             dm->fPointer = NULL;
    163             SkASSERT(fUsed >= dm->fBytes);
    164             fUsed -= dm->fBytes;
    165             cur = iter.prev();
    166             // Purged DMs are taken out of the list.  This saves times
    167             // looking them up.  Purged DMs are NOT deleted.
    168             fList.remove(dm);
    169         } else {
    170             cur = iter.prev();
    171         }
    172     }
    173 }
    174 
    175 SkDiscardableMemory* DiscardableMemoryPool::create(size_t bytes) {
    176     void* addr = sk_malloc_flags(bytes, 0);
    177     if (NULL == addr) {
    178         return NULL;
    179     }
    180     PoolDiscardableMemory* dm = SkNEW_ARGS(PoolDiscardableMemory,
    181                                              (this, addr, bytes));
    182     SkAutoMutexAcquire autoMutexAcquire(fMutex);
    183     fList.addToHead(dm);
    184     fUsed += bytes;
    185     this->dumpDownTo(fBudget);
    186     return dm;
    187 }
    188 
    189 void DiscardableMemoryPool::free(PoolDiscardableMemory* dm) {
    190     // This is called by dm's destructor.
    191     if (dm->fPointer != NULL) {
    192         SkAutoMutexAcquire autoMutexAcquire(fMutex);
    193         sk_free(dm->fPointer);
    194         dm->fPointer = NULL;
    195         SkASSERT(fUsed >= dm->fBytes);
    196         fUsed -= dm->fBytes;
    197         fList.remove(dm);
    198     } else {
    199         SkASSERT(!fList.isInList(dm));
    200     }
    201 }
    202 
    203 bool DiscardableMemoryPool::lock(PoolDiscardableMemory* dm) {
    204     SkASSERT(dm != NULL);
    205     if (NULL == dm->fPointer) {
    206         #if SK_LAZY_CACHE_STATS
    207         SkAutoMutexAcquire autoMutexAcquire(fMutex);
    208         ++fCacheMisses;
    209         #endif  // SK_LAZY_CACHE_STATS
    210         return false;
    211     }
    212     SkAutoMutexAcquire autoMutexAcquire(fMutex);
    213     if (NULL == dm->fPointer) {
    214         // May have been purged while waiting for lock.
    215         #if SK_LAZY_CACHE_STATS
    216         ++fCacheMisses;
    217         #endif  // SK_LAZY_CACHE_STATS
    218         return false;
    219     }
    220     dm->fLocked = true;
    221     fList.remove(dm);
    222     fList.addToHead(dm);
    223     #if SK_LAZY_CACHE_STATS
    224     ++fCacheHits;
    225     #endif  // SK_LAZY_CACHE_STATS
    226     return true;
    227 }
    228 
    229 void DiscardableMemoryPool::unlock(PoolDiscardableMemory* dm) {
    230     SkASSERT(dm != NULL);
    231     SkAutoMutexAcquire autoMutexAcquire(fMutex);
    232     dm->fLocked = false;
    233     this->dumpDownTo(fBudget);
    234 }
    235 
    236 size_t DiscardableMemoryPool::getRAMUsed() {
    237     return fUsed;
    238 }
    239 void DiscardableMemoryPool::setRAMBudget(size_t budget) {
    240     SkAutoMutexAcquire autoMutexAcquire(fMutex);
    241     fBudget = budget;
    242     this->dumpDownTo(fBudget);
    243 }
    244 void DiscardableMemoryPool::dumpPool() {
    245     SkAutoMutexAcquire autoMutexAcquire(fMutex);
    246     this->dumpDownTo(0);
    247 }
    248 
    249 ////////////////////////////////////////////////////////////////////////////////
    250 SK_DECLARE_STATIC_MUTEX(gMutex);
    251 SkDiscardableMemoryPool* create_global_pool() {
    252     return SkDiscardableMemoryPool::Create(SK_DEFAULT_GLOBAL_DISCARDABLE_MEMORY_POOL_SIZE,
    253                                            &gMutex);
    254 }
    255 
    256 }  // namespace
    257 
    258 SkDiscardableMemoryPool* SkDiscardableMemoryPool::Create(size_t size, SkBaseMutex* mutex) {
    259     return SkNEW_ARGS(DiscardableMemoryPool, (size, mutex));
    260 }
    261 
    262 SkDiscardableMemoryPool* SkGetGlobalDiscardableMemoryPool() {
    263     SK_DECLARE_STATIC_LAZY_PTR(SkDiscardableMemoryPool, global, create_global_pool);
    264     return global.get();
    265 }
    266 
    267 // defined in SkImageGenerator.h
    268 void SkPurgeGlobalDiscardableMemoryPool() {
    269     SkGetGlobalDiscardableMemoryPool()->dumpPool();
    270 }
    271 ////////////////////////////////////////////////////////////////////////////////
    272