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