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