SkDiscardableMemoryPool.cpp 7.3 KB

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