• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "include/private/SkMalloc.h"
9  #include "include/private/SkMutex.h"
10  #include "include/private/SkTemplates.h"
11  #include "src/core/SkDiscardableMemory.h"
12  #include "src/core/SkMakeUnique.h"
13  #include "src/core/SkTInternalLList.h"
14  #include "src/lazy/SkDiscardableMemoryPool.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);
create(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;
getRAMBudget()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
getCacheHits()46      int getCacheHits() override { return fCacheHits; }
getCacheMisses()47      int getCacheMisses() override { return fCacheMisses; }
resetCacheHitsAndMisses()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  
PoolDiscardableMemory(sk_sp<DiscardableMemoryPool> pool,SkAutoFree pointer,size_t bytes)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  
~PoolDiscardableMemory()104  PoolDiscardableMemory::~PoolDiscardableMemory() {
105      SkASSERT(!fLocked); // contract for SkDiscardableMemory
106      fPool->removeFromPool(this);
107  }
108  
lock()109  bool PoolDiscardableMemory::lock() {
110      SkASSERT(!fLocked); // contract for SkDiscardableMemory
111      return fPool->lock(this);
112  }
113  
data()114  void* PoolDiscardableMemory::data() {
115      SkASSERT(fLocked); // contract for SkDiscardableMemory
116      return fPointer.get();
117  }
118  
unlock()119  void PoolDiscardableMemory::unlock() {
120      SkASSERT(fLocked); // contract for SkDiscardableMemory
121      fPool->unlock(this);
122  }
123  
124  ////////////////////////////////////////////////////////////////////////////////
125  
DiscardableMemoryPool(size_t budget)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  }
~DiscardableMemoryPool()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  
dumpDownTo(size_t budget)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  
make(size_t bytes)166  std::unique_ptr<SkDiscardableMemory> DiscardableMemoryPool::make(size_t bytes) {
167      SkAutoFree addr(sk_malloc_canfail(bytes));
168      if (nullptr == addr) {
169          return nullptr;
170      }
171      auto dm = skstd::make_unique<PoolDiscardableMemory>(sk_ref_sp(this), std::move(addr), bytes);
172      SkAutoMutexExclusive autoMutexAcquire(fMutex);
173      fList.addToHead(dm.get());
174      fUsed += bytes;
175      this->dumpDownTo(fBudget);
176      return dm;
177  }
178  
removeFromPool(PoolDiscardableMemory * dm)179  void DiscardableMemoryPool::removeFromPool(PoolDiscardableMemory* dm) {
180      SkAutoMutexExclusive 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  
lock(PoolDiscardableMemory * dm)191  bool DiscardableMemoryPool::lock(PoolDiscardableMemory* dm) {
192      SkASSERT(dm != nullptr);
193      SkAutoMutexExclusive 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  
unlock(PoolDiscardableMemory * dm)210  void DiscardableMemoryPool::unlock(PoolDiscardableMemory* dm) {
211      SkASSERT(dm != nullptr);
212      SkAutoMutexExclusive autoMutexAcquire(fMutex);
213      dm->fLocked = false;
214      this->dumpDownTo(fBudget);
215  }
216  
getRAMUsed()217  size_t DiscardableMemoryPool::getRAMUsed() {
218      return fUsed;
219  }
setRAMBudget(size_t budget)220  void DiscardableMemoryPool::setRAMBudget(size_t budget) {
221      SkAutoMutexExclusive autoMutexAcquire(fMutex);
222      fBudget = budget;
223      this->dumpDownTo(fBudget);
224  }
dumpPool()225  void DiscardableMemoryPool::dumpPool() {
226      SkAutoMutexExclusive autoMutexAcquire(fMutex);
227      this->dumpDownTo(0);
228  }
229  
230  }  // namespace
231  
Make(size_t size)232  sk_sp<SkDiscardableMemoryPool> SkDiscardableMemoryPool::Make(size_t size) {
233      return sk_make_sp<DiscardableMemoryPool>(size);
234  }
235  
SkGetGlobalDiscardableMemoryPool()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