blob: 78c48f50c158f25ea90a87300e9c6bddc9816d3d [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"
mtklein1b249332015-07-07 12:21:21 -070011#include "SkMutex.h"
mtklein6c59d802015-09-09 09:09:53 -070012#include "SkOncePtr.h"
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +000013#include "SkTInternalLList.h"
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +000014
15// Note:
16// A PoolDiscardableMemory is memory that is counted in a pool.
17// A DiscardableMemoryPool is a pool of PoolDiscardableMemorys.
18
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +000019namespace {
20
21class PoolDiscardableMemory;
22
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +000023/**
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +000024 * This non-global pool can be used for unit tests to verify that the
25 * pool works.
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +000026 */
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +000027class DiscardableMemoryPool : public SkDiscardableMemoryPool {
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +000028public:
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +000029 /**
30 * Without mutex, will be not be thread safe.
31 */
halcanary96fcdcc2015-08-27 07:41:13 -070032 DiscardableMemoryPool(size_t budget, SkBaseMutex* mutex = nullptr);
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +000033 virtual ~DiscardableMemoryPool();
34
mtklein36352bf2015-03-25 18:17:31 -070035 SkDiscardableMemory* create(size_t bytes) override;
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +000036
mtklein36352bf2015-03-25 18:17:31 -070037 size_t getRAMUsed() override;
38 void setRAMBudget(size_t budget) override;
39 size_t getRAMBudget() override { return fBudget; }
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +000040
41 /** purges all unlocked DMs */
mtklein36352bf2015-03-25 18:17:31 -070042 void dumpPool() override;
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +000043
44 #if SK_LAZY_CACHE_STATS // Defined in SkDiscardableMemoryPool.h
mtklein36352bf2015-03-25 18:17:31 -070045 int getCacheHits() override { return fCacheHits; }
46 int getCacheMisses() override { return fCacheMisses; }
47 void resetCacheHitsAndMisses() override {
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +000048 fCacheHits = fCacheMisses = 0;
49 }
50 int fCacheHits;
51 int fCacheMisses;
52 #endif // SK_LAZY_CACHE_STATS
53
54private:
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 */
78class PoolDiscardableMemory : public SkDiscardableMemory {
79public:
80 PoolDiscardableMemory(DiscardableMemoryPool* pool,
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +000081 void* pointer, size_t bytes);
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +000082 virtual ~PoolDiscardableMemory();
mtklein36352bf2015-03-25 18:17:31 -070083 bool lock() override;
84 void* data() override;
85 void unlock() override;
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +000086 friend class DiscardableMemoryPool;
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +000087private:
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +000088 SK_DECLARE_INTERNAL_LLIST_INTERFACE(PoolDiscardableMemory);
89 DiscardableMemoryPool* const fPool;
90 bool fLocked;
91 void* fPointer;
92 const size_t fBytes;
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +000093};
94
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +000095PoolDiscardableMemory::PoolDiscardableMemory(DiscardableMemoryPool* pool,
96 void* pointer,
97 size_t bytes)
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +000098 : fPool(pool)
99 , fLocked(true)
100 , fPointer(pointer)
101 , fBytes(bytes) {
halcanary96fcdcc2015-08-27 07:41:13 -0700102 SkASSERT(fPool != nullptr);
103 SkASSERT(fPointer != nullptr);
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000104 SkASSERT(fBytes > 0);
105 fPool->ref();
106}
107
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000108PoolDiscardableMemory::~PoolDiscardableMemory() {
reed@google.com1d0654f2013-12-12 22:37:32 +0000109 SkASSERT(!fLocked); // contract for SkDiscardableMemory
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000110 fPool->free(this);
111 fPool->unref();
112}
113
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000114bool PoolDiscardableMemory::lock() {
reed@google.com1d0654f2013-12-12 22:37:32 +0000115 SkASSERT(!fLocked); // contract for SkDiscardableMemory
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000116 return fPool->lock(this);
117}
118
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000119void* PoolDiscardableMemory::data() {
reed@google.com1d0654f2013-12-12 22:37:32 +0000120 SkASSERT(fLocked); // contract for SkDiscardableMemory
121 return fPointer;
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000122}
123
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000124void PoolDiscardableMemory::unlock() {
reed@google.com1d0654f2013-12-12 22:37:32 +0000125 SkASSERT(fLocked); // contract for SkDiscardableMemory
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000126 fPool->unlock(this);
127}
128
129////////////////////////////////////////////////////////////////////////////////
130
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000131DiscardableMemoryPool::DiscardableMemoryPool(size_t budget,
132 SkBaseMutex* mutex)
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000133 : fMutex(mutex)
134 , fBudget(budget)
135 , fUsed(0) {
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000136 #if SK_LAZY_CACHE_STATS
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000137 fCacheHits = 0;
138 fCacheMisses = 0;
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000139 #endif // SK_LAZY_CACHE_STATS
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000140}
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000141DiscardableMemoryPool::~DiscardableMemoryPool() {
142 // PoolDiscardableMemory objects that belong to this pool are
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000143 // always deleted before deleting this pool since each one has a
144 // ref to the pool.
145 SkASSERT(fList.isEmpty());
146}
147
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000148void DiscardableMemoryPool::dumpDownTo(size_t budget) {
halcanary96fcdcc2015-08-27 07:41:13 -0700149 if (fMutex != nullptr) {
halcanary9b790662014-06-11 09:40:23 -0700150 fMutex->assertHeld();
151 }
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000152 if (fUsed <= budget) {
153 return;
154 }
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000155 typedef SkTInternalLList<PoolDiscardableMemory>::Iter Iter;
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000156 Iter iter;
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000157 PoolDiscardableMemory* cur = iter.init(fList, Iter::kTail_IterStart);
bsalomon49f085d2014-09-05 13:34:00 -0700158 while ((fUsed > budget) && (cur)) {
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000159 if (!cur->fLocked) {
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000160 PoolDiscardableMemory* dm = cur;
halcanary96fcdcc2015-08-27 07:41:13 -0700161 SkASSERT(dm->fPointer != nullptr);
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000162 sk_free(dm->fPointer);
halcanary96fcdcc2015-08-27 07:41:13 -0700163 dm->fPointer = nullptr;
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000164 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
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000176SkDiscardableMemory* DiscardableMemoryPool::create(size_t bytes) {
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000177 void* addr = sk_malloc_flags(bytes, 0);
halcanary96fcdcc2015-08-27 07:41:13 -0700178 if (nullptr == addr) {
179 return nullptr;
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000180 }
halcanary385fe4d2015-08-26 13:07:48 -0700181 PoolDiscardableMemory* dm = new PoolDiscardableMemory(this, addr, bytes);
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000182 SkAutoMutexAcquire autoMutexAcquire(fMutex);
183 fList.addToHead(dm);
184 fUsed += bytes;
185 this->dumpDownTo(fBudget);
186 return dm;
187}
188
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000189void DiscardableMemoryPool::free(PoolDiscardableMemory* dm) {
halcanary2edf5992015-03-26 14:08:56 -0700190 SkAutoMutexAcquire autoMutexAcquire(fMutex);
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000191 // This is called by dm's destructor.
halcanary96fcdcc2015-08-27 07:41:13 -0700192 if (dm->fPointer != nullptr) {
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000193 sk_free(dm->fPointer);
halcanary96fcdcc2015-08-27 07:41:13 -0700194 dm->fPointer = nullptr;
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000195 SkASSERT(fUsed >= dm->fBytes);
196 fUsed -= dm->fBytes;
197 fList.remove(dm);
198 } else {
199 SkASSERT(!fList.isInList(dm));
200 }
201}
202
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000203bool DiscardableMemoryPool::lock(PoolDiscardableMemory* dm) {
halcanary96fcdcc2015-08-27 07:41:13 -0700204 SkASSERT(dm != nullptr);
205 if (nullptr == dm->fPointer) {
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000206 #if SK_LAZY_CACHE_STATS
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000207 SkAutoMutexAcquire autoMutexAcquire(fMutex);
208 ++fCacheMisses;
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000209 #endif // SK_LAZY_CACHE_STATS
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000210 return false;
211 }
212 SkAutoMutexAcquire autoMutexAcquire(fMutex);
halcanary96fcdcc2015-08-27 07:41:13 -0700213 if (nullptr == dm->fPointer) {
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000214 // May have been purged while waiting for lock.
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000215 #if SK_LAZY_CACHE_STATS
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000216 ++fCacheMisses;
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000217 #endif // SK_LAZY_CACHE_STATS
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000218 return false;
219 }
220 dm->fLocked = true;
221 fList.remove(dm);
222 fList.addToHead(dm);
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000223 #if SK_LAZY_CACHE_STATS
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000224 ++fCacheHits;
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000225 #endif // SK_LAZY_CACHE_STATS
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000226 return true;
227}
228
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000229void DiscardableMemoryPool::unlock(PoolDiscardableMemory* dm) {
halcanary96fcdcc2015-08-27 07:41:13 -0700230 SkASSERT(dm != nullptr);
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000231 SkAutoMutexAcquire autoMutexAcquire(fMutex);
232 dm->fLocked = false;
233 this->dumpDownTo(fBudget);
234}
235
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000236size_t DiscardableMemoryPool::getRAMUsed() {
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000237 return fUsed;
238}
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000239void DiscardableMemoryPool::setRAMBudget(size_t budget) {
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000240 SkAutoMutexAcquire autoMutexAcquire(fMutex);
241 fBudget = budget;
242 this->dumpDownTo(fBudget);
243}
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000244void DiscardableMemoryPool::dumpPool() {
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000245 SkAutoMutexAcquire autoMutexAcquire(fMutex);
246 this->dumpDownTo(0);
247}
248
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000249} // namespace
250
mtklein78358bf2014-06-02 08:44:27 -0700251SkDiscardableMemoryPool* SkDiscardableMemoryPool::Create(size_t size, SkBaseMutex* mutex) {
halcanary385fe4d2015-08-26 13:07:48 -0700252 return new DiscardableMemoryPool(size, mutex);
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000253}
254
mtklein6c59d802015-09-09 09:09:53 -0700255SK_DECLARE_STATIC_MUTEX(gMutex);
256SK_DECLARE_STATIC_ONCE_PTR(SkDiscardableMemoryPool, global);
mtklein148ec592014-10-13 13:17:56 -0700257
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000258SkDiscardableMemoryPool* SkGetGlobalDiscardableMemoryPool() {
mtklein6c59d802015-09-09 09:09:53 -0700259 return global.get([] {
260 return SkDiscardableMemoryPool::Create(SK_DEFAULT_GLOBAL_DISCARDABLE_MEMORY_POOL_SIZE,
261 &gMutex);
262 });
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000263}