blob: 2cb1b3f22bae1eb583c62dc568c1a810201788cf [file] [log] [blame]
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +00001/*
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
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +00008#include "SkDiscardableMemory.h"
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +00009#include "SkDiscardableMemoryPool.h"
halcanaryfa37a212014-07-02 10:43:50 -070010#include "SkImageGenerator.h"
Herb Derbyd7b34a52017-03-20 11:19:23 -040011#include "SkMemory.h"
mtklein1b249332015-07-07 12:21:21 -070012#include "SkMutex.h"
mtkleinffa4a922016-05-05 16:05:56 -070013#include "SkOnce.h"
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +000014#include "SkTInternalLList.h"
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +000015
16// Note:
17// A PoolDiscardableMemory is memory that is counted in a pool.
18// A DiscardableMemoryPool is a pool of PoolDiscardableMemorys.
19
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +000020namespace {
21
22class PoolDiscardableMemory;
23
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +000024/**
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +000025 * This non-global pool can be used for unit tests to verify that the
26 * pool works.
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +000027 */
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +000028class DiscardableMemoryPool : public SkDiscardableMemoryPool {
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +000029public:
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +000030 /**
31 * Without mutex, will be not be thread safe.
32 */
sclittled9f5d202016-05-04 18:23:30 -070033 DiscardableMemoryPool(size_t budget, SkBaseMutex* mutex = nullptr);
Brian Salomond3b65972017-03-22 12:05:03 -040034 ~DiscardableMemoryPool() override;
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +000035
mtklein36352bf2015-03-25 18:17:31 -070036 SkDiscardableMemory* create(size_t bytes) override;
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +000037
mtklein36352bf2015-03-25 18:17:31 -070038 size_t getRAMUsed() override;
39 void setRAMBudget(size_t budget) override;
40 size_t getRAMBudget() override { return fBudget; }
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +000041
42 /** purges all unlocked DMs */
mtklein36352bf2015-03-25 18:17:31 -070043 void dumpPool() override;
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +000044
45 #if SK_LAZY_CACHE_STATS // Defined in SkDiscardableMemoryPool.h
mtklein36352bf2015-03-25 18:17:31 -070046 int getCacheHits() override { return fCacheHits; }
47 int getCacheMisses() override { return fCacheMisses; }
48 void resetCacheHitsAndMisses() override {
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +000049 fCacheHits = fCacheMisses = 0;
50 }
51 int fCacheHits;
52 int fCacheMisses;
53 #endif // SK_LAZY_CACHE_STATS
54
55private:
sclittled9f5d202016-05-04 18:23:30 -070056 SkBaseMutex* fMutex;
57 size_t fBudget;
58 size_t fUsed;
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +000059 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 free(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 */
79class PoolDiscardableMemory : public SkDiscardableMemory {
80public:
81 PoolDiscardableMemory(DiscardableMemoryPool* pool,
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +000082 void* pointer, size_t bytes);
Brian Salomond3b65972017-03-22 12:05:03 -040083 ~PoolDiscardableMemory() override;
mtklein36352bf2015-03-25 18:17:31 -070084 bool lock() override;
85 void* data() override;
86 void unlock() override;
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +000087 friend class DiscardableMemoryPool;
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +000088private:
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +000089 SK_DECLARE_INTERNAL_LLIST_INTERFACE(PoolDiscardableMemory);
90 DiscardableMemoryPool* const fPool;
91 bool fLocked;
92 void* fPointer;
93 const size_t fBytes;
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +000094};
95
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +000096PoolDiscardableMemory::PoolDiscardableMemory(DiscardableMemoryPool* pool,
97 void* pointer,
98 size_t bytes)
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +000099 : fPool(pool)
100 , fLocked(true)
101 , fPointer(pointer)
102 , fBytes(bytes) {
halcanary96fcdcc2015-08-27 07:41:13 -0700103 SkASSERT(fPool != nullptr);
104 SkASSERT(fPointer != nullptr);
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000105 SkASSERT(fBytes > 0);
106 fPool->ref();
107}
108
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000109PoolDiscardableMemory::~PoolDiscardableMemory() {
reed@google.com1d0654f2013-12-12 22:37:32 +0000110 SkASSERT(!fLocked); // contract for SkDiscardableMemory
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000111 fPool->free(this);
112 fPool->unref();
113}
114
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000115bool PoolDiscardableMemory::lock() {
reed@google.com1d0654f2013-12-12 22:37:32 +0000116 SkASSERT(!fLocked); // contract for SkDiscardableMemory
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000117 return fPool->lock(this);
118}
119
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000120void* PoolDiscardableMemory::data() {
reed@google.com1d0654f2013-12-12 22:37:32 +0000121 SkASSERT(fLocked); // contract for SkDiscardableMemory
122 return fPointer;
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000123}
124
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000125void PoolDiscardableMemory::unlock() {
reed@google.com1d0654f2013-12-12 22:37:32 +0000126 SkASSERT(fLocked); // contract for SkDiscardableMemory
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000127 fPool->unlock(this);
128}
129
130////////////////////////////////////////////////////////////////////////////////
131
sclittled9f5d202016-05-04 18:23:30 -0700132DiscardableMemoryPool::DiscardableMemoryPool(size_t budget,
133 SkBaseMutex* mutex)
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000134 : fMutex(mutex)
135 , fBudget(budget)
136 , fUsed(0) {
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000137 #if SK_LAZY_CACHE_STATS
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000138 fCacheHits = 0;
139 fCacheMisses = 0;
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000140 #endif // SK_LAZY_CACHE_STATS
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000141}
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000142DiscardableMemoryPool::~DiscardableMemoryPool() {
143 // PoolDiscardableMemory objects that belong to this pool are
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000144 // always deleted before deleting this pool since each one has a
145 // ref to the pool.
146 SkASSERT(fList.isEmpty());
147}
148
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000149void DiscardableMemoryPool::dumpDownTo(size_t budget) {
halcanary96fcdcc2015-08-27 07:41:13 -0700150 if (fMutex != nullptr) {
halcanary9b790662014-06-11 09:40:23 -0700151 fMutex->assertHeld();
152 }
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000153 if (fUsed <= budget) {
154 return;
155 }
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000156 typedef SkTInternalLList<PoolDiscardableMemory>::Iter Iter;
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000157 Iter iter;
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000158 PoolDiscardableMemory* cur = iter.init(fList, Iter::kTail_IterStart);
bsalomon49f085d2014-09-05 13:34:00 -0700159 while ((fUsed > budget) && (cur)) {
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000160 if (!cur->fLocked) {
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000161 PoolDiscardableMemory* dm = cur;
halcanary96fcdcc2015-08-27 07:41:13 -0700162 SkASSERT(dm->fPointer != nullptr);
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000163 sk_free(dm->fPointer);
halcanary96fcdcc2015-08-27 07:41:13 -0700164 dm->fPointer = nullptr;
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000165 SkASSERT(fUsed >= dm->fBytes);
166 fUsed -= dm->fBytes;
167 cur = iter.prev();
168 // Purged DMs are taken out of the list. This saves times
169 // looking them up. Purged DMs are NOT deleted.
170 fList.remove(dm);
171 } else {
172 cur = iter.prev();
173 }
174 }
175}
176
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000177SkDiscardableMemory* DiscardableMemoryPool::create(size_t bytes) {
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000178 void* addr = sk_malloc_flags(bytes, 0);
halcanary96fcdcc2015-08-27 07:41:13 -0700179 if (nullptr == addr) {
180 return nullptr;
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000181 }
halcanary385fe4d2015-08-26 13:07:48 -0700182 PoolDiscardableMemory* dm = new PoolDiscardableMemory(this, addr, bytes);
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000183 SkAutoMutexAcquire autoMutexAcquire(fMutex);
184 fList.addToHead(dm);
185 fUsed += bytes;
186 this->dumpDownTo(fBudget);
187 return dm;
188}
189
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000190void DiscardableMemoryPool::free(PoolDiscardableMemory* dm) {
halcanary2edf5992015-03-26 14:08:56 -0700191 SkAutoMutexAcquire autoMutexAcquire(fMutex);
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000192 // This is called by dm's destructor.
halcanary96fcdcc2015-08-27 07:41:13 -0700193 if (dm->fPointer != nullptr) {
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000194 sk_free(dm->fPointer);
halcanary96fcdcc2015-08-27 07:41:13 -0700195 dm->fPointer = nullptr;
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000196 SkASSERT(fUsed >= dm->fBytes);
197 fUsed -= dm->fBytes;
198 fList.remove(dm);
199 } else {
200 SkASSERT(!fList.isInList(dm));
201 }
202}
203
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000204bool DiscardableMemoryPool::lock(PoolDiscardableMemory* dm) {
halcanary96fcdcc2015-08-27 07:41:13 -0700205 SkASSERT(dm != nullptr);
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000206 SkAutoMutexAcquire autoMutexAcquire(fMutex);
halcanary96fcdcc2015-08-27 07:41:13 -0700207 if (nullptr == dm->fPointer) {
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000208 // May have been purged while waiting for lock.
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000209 #if SK_LAZY_CACHE_STATS
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000210 ++fCacheMisses;
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000211 #endif // SK_LAZY_CACHE_STATS
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000212 return false;
213 }
214 dm->fLocked = true;
215 fList.remove(dm);
216 fList.addToHead(dm);
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000217 #if SK_LAZY_CACHE_STATS
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000218 ++fCacheHits;
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000219 #endif // SK_LAZY_CACHE_STATS
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000220 return true;
221}
222
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000223void DiscardableMemoryPool::unlock(PoolDiscardableMemory* dm) {
halcanary96fcdcc2015-08-27 07:41:13 -0700224 SkASSERT(dm != nullptr);
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000225 SkAutoMutexAcquire autoMutexAcquire(fMutex);
226 dm->fLocked = false;
227 this->dumpDownTo(fBudget);
228}
229
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000230size_t DiscardableMemoryPool::getRAMUsed() {
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000231 return fUsed;
232}
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000233void DiscardableMemoryPool::setRAMBudget(size_t budget) {
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000234 SkAutoMutexAcquire autoMutexAcquire(fMutex);
235 fBudget = budget;
236 this->dumpDownTo(fBudget);
237}
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000238void DiscardableMemoryPool::dumpPool() {
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000239 SkAutoMutexAcquire autoMutexAcquire(fMutex);
240 this->dumpDownTo(0);
241}
242
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000243} // namespace
244
sclittled9f5d202016-05-04 18:23:30 -0700245SkDiscardableMemoryPool* SkDiscardableMemoryPool::Create(size_t size, SkBaseMutex* mutex) {
halcanary385fe4d2015-08-26 13:07:48 -0700246 return new DiscardableMemoryPool(size, mutex);
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000247}
248
reed086eea92016-05-04 17:12:46 -0700249SK_DECLARE_STATIC_MUTEX(gMutex);
mtklein148ec592014-10-13 13:17:56 -0700250
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000251SkDiscardableMemoryPool* SkGetGlobalDiscardableMemoryPool() {
mtkleinffa4a922016-05-05 16:05:56 -0700252 static SkOnce once;
253 static SkDiscardableMemoryPool* global;
254 once([]{
255 global = SkDiscardableMemoryPool::Create(SK_DEFAULT_GLOBAL_DISCARDABLE_MEMORY_POOL_SIZE,
256 &gMutex);
mtklein6c59d802015-09-09 09:09:53 -0700257 });
mtkleinffa4a922016-05-05 16:05:56 -0700258 return global;
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000259}