blob: 394e3257075e9d3d0eb40c1e54da812f884c96b3 [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
8#include "SkDiscardableMemoryPool.h"
Hal Canarya294be22017-04-19 13:17:59 -04009#include "SkDiscardableMemory.h"
10#include "SkMakeUnique.h"
Herb Derbyb549cc32017-03-27 13:35:15 -040011#include "SkMalloc.h"
mtklein1b249332015-07-07 12:21:21 -070012#include "SkMutex.h"
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +000013#include "SkTInternalLList.h"
Hal Canarya294be22017-04-19 13:17:59 -040014#include "SkTemplates.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:
Hal Canary788c3c42017-04-25 08:58:57 -040030 DiscardableMemoryPool(size_t budget);
Brian Salomond3b65972017-03-22 12:05:03 -040031 ~DiscardableMemoryPool() override;
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +000032
Hal Canarya294be22017-04-19 13:17:59 -040033 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 }
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:
Hal Canary788c3c42017-04-25 08:58:57 -040056 SkMutex fMutex;
sclittled9f5d202016-05-04 18:23:30 -070057 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 */
Hal Canarya294be22017-04-19 13:17:59 -040064 void removeFromPool(PoolDiscardableMemory* dm);
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +000065 /** 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:
Hal Canarya294be22017-04-19 13:17:59 -040081 PoolDiscardableMemory(sk_sp<DiscardableMemoryPool> pool, SkAutoFree pointer, size_t bytes);
Brian Salomond3b65972017-03-22 12:05:03 -040082 ~PoolDiscardableMemory() override;
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);
Hal Canarya294be22017-04-19 13:17:59 -040089 sk_sp<DiscardableMemoryPool> fPool;
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +000090 bool fLocked;
Hal Canarya294be22017-04-19 13:17:59 -040091 SkAutoFree fPointer;
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +000092 const size_t fBytes;
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +000093};
94
Hal Canarya294be22017-04-19 13:17:59 -040095PoolDiscardableMemory::PoolDiscardableMemory(sk_sp<DiscardableMemoryPool> pool,
96 SkAutoFree pointer,
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +000097 size_t bytes)
Hal Canarya294be22017-04-19 13:17:59 -040098 : fPool(std::move(pool)), fLocked(true), fPointer(std::move(pointer)), fBytes(bytes) {
halcanary96fcdcc2015-08-27 07:41:13 -070099 SkASSERT(fPool != nullptr);
100 SkASSERT(fPointer != nullptr);
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000101 SkASSERT(fBytes > 0);
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000102}
103
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000104PoolDiscardableMemory::~PoolDiscardableMemory() {
reed@google.com1d0654f2013-12-12 22:37:32 +0000105 SkASSERT(!fLocked); // contract for SkDiscardableMemory
Hal Canarya294be22017-04-19 13:17:59 -0400106 fPool->removeFromPool(this);
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000107}
108
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000109bool PoolDiscardableMemory::lock() {
reed@google.com1d0654f2013-12-12 22:37:32 +0000110 SkASSERT(!fLocked); // contract for SkDiscardableMemory
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000111 return fPool->lock(this);
112}
113
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000114void* PoolDiscardableMemory::data() {
reed@google.com1d0654f2013-12-12 22:37:32 +0000115 SkASSERT(fLocked); // contract for SkDiscardableMemory
Hal Canarya294be22017-04-19 13:17:59 -0400116 return fPointer.get();
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000117}
118
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000119void PoolDiscardableMemory::unlock() {
reed@google.com1d0654f2013-12-12 22:37:32 +0000120 SkASSERT(fLocked); // contract for SkDiscardableMemory
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000121 fPool->unlock(this);
122}
123
124////////////////////////////////////////////////////////////////////////////////
125
Hal Canary788c3c42017-04-25 08:58:57 -0400126DiscardableMemoryPool::DiscardableMemoryPool(size_t budget)
127 : fBudget(budget)
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000128 , fUsed(0) {
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000129 #if SK_LAZY_CACHE_STATS
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000130 fCacheHits = 0;
131 fCacheMisses = 0;
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000132 #endif // SK_LAZY_CACHE_STATS
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000133}
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000134DiscardableMemoryPool::~DiscardableMemoryPool() {
135 // PoolDiscardableMemory objects that belong to this pool are
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000136 // always deleted before deleting this pool since each one has a
137 // ref to the pool.
138 SkASSERT(fList.isEmpty());
139}
140
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000141void DiscardableMemoryPool::dumpDownTo(size_t budget) {
Hal Canary788c3c42017-04-25 08:58:57 -0400142 fMutex.assertHeld();
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000143 if (fUsed <= budget) {
144 return;
145 }
Hal Canarya294be22017-04-19 13:17:59 -0400146 using Iter = SkTInternalLList<PoolDiscardableMemory>::Iter;
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000147 Iter iter;
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000148 PoolDiscardableMemory* cur = iter.init(fList, Iter::kTail_IterStart);
bsalomon49f085d2014-09-05 13:34:00 -0700149 while ((fUsed > budget) && (cur)) {
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000150 if (!cur->fLocked) {
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000151 PoolDiscardableMemory* dm = cur;
halcanary96fcdcc2015-08-27 07:41:13 -0700152 SkASSERT(dm->fPointer != nullptr);
halcanary96fcdcc2015-08-27 07:41:13 -0700153 dm->fPointer = nullptr;
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000154 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
Hal Canarya294be22017-04-19 13:17:59 -0400166std::unique_ptr<SkDiscardableMemory> DiscardableMemoryPool::make(size_t bytes) {
Mike Reed8dc8dbc2018-01-05 11:20:10 -0500167 SkAutoFree addr(sk_malloc_canfail(bytes));
halcanary96fcdcc2015-08-27 07:41:13 -0700168 if (nullptr == addr) {
169 return nullptr;
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000170 }
Hal Canarya294be22017-04-19 13:17:59 -0400171 auto dm = skstd::make_unique<PoolDiscardableMemory>(sk_ref_sp(this), std::move(addr), bytes);
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000172 SkAutoMutexAcquire autoMutexAcquire(fMutex);
Hal Canarya294be22017-04-19 13:17:59 -0400173 fList.addToHead(dm.get());
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000174 fUsed += bytes;
175 this->dumpDownTo(fBudget);
Hal Canarya294be22017-04-19 13:17:59 -0400176 return std::move(dm);
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000177}
178
Hal Canarya294be22017-04-19 13:17:59 -0400179void DiscardableMemoryPool::removeFromPool(PoolDiscardableMemory* dm) {
halcanary2edf5992015-03-26 14:08:56 -0700180 SkAutoMutexAcquire autoMutexAcquire(fMutex);
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000181 // This is called by dm's destructor.
halcanary96fcdcc2015-08-27 07:41:13 -0700182 if (dm->fPointer != nullptr) {
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000183 SkASSERT(fUsed >= dm->fBytes);
184 fUsed -= dm->fBytes;
185 fList.remove(dm);
186 } else {
187 SkASSERT(!fList.isInList(dm));
188 }
189}
190
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000191bool DiscardableMemoryPool::lock(PoolDiscardableMemory* dm) {
halcanary96fcdcc2015-08-27 07:41:13 -0700192 SkASSERT(dm != nullptr);
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000193 SkAutoMutexAcquire autoMutexAcquire(fMutex);
halcanary96fcdcc2015-08-27 07:41:13 -0700194 if (nullptr == dm->fPointer) {
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000195 // May have been purged while waiting for lock.
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000196 #if SK_LAZY_CACHE_STATS
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000197 ++fCacheMisses;
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000198 #endif // SK_LAZY_CACHE_STATS
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000199 return false;
200 }
201 dm->fLocked = true;
202 fList.remove(dm);
203 fList.addToHead(dm);
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000204 #if SK_LAZY_CACHE_STATS
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000205 ++fCacheHits;
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000206 #endif // SK_LAZY_CACHE_STATS
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000207 return true;
208}
209
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000210void DiscardableMemoryPool::unlock(PoolDiscardableMemory* dm) {
halcanary96fcdcc2015-08-27 07:41:13 -0700211 SkASSERT(dm != nullptr);
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000212 SkAutoMutexAcquire autoMutexAcquire(fMutex);
213 dm->fLocked = false;
214 this->dumpDownTo(fBudget);
215}
216
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000217size_t DiscardableMemoryPool::getRAMUsed() {
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000218 return fUsed;
219}
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000220void DiscardableMemoryPool::setRAMBudget(size_t budget) {
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000221 SkAutoMutexAcquire autoMutexAcquire(fMutex);
222 fBudget = budget;
223 this->dumpDownTo(fBudget);
224}
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000225void DiscardableMemoryPool::dumpPool() {
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000226 SkAutoMutexAcquire autoMutexAcquire(fMutex);
227 this->dumpDownTo(0);
228}
229
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000230} // namespace
231
Hal Canary788c3c42017-04-25 08:58:57 -0400232sk_sp<SkDiscardableMemoryPool> SkDiscardableMemoryPool::Make(size_t size) {
233 return sk_make_sp<DiscardableMemoryPool>(size);
commit-bot@chromium.orgcf2f0082014-04-04 16:43:38 +0000234}
235
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000236SkDiscardableMemoryPool* SkGetGlobalDiscardableMemoryPool() {
Hal Canarya294be22017-04-19 13:17:59 -0400237 // Intentionally leak this global pool.
238 static SkDiscardableMemoryPool* global =
Hal Canary788c3c42017-04-25 08:58:57 -0400239 new DiscardableMemoryPool(SK_DEFAULT_GLOBAL_DISCARDABLE_MEMORY_POOL_SIZE);
mtkleinffa4a922016-05-05 16:05:56 -0700240 return global;
halcanary@google.com2c7c7ee2013-12-05 18:31:42 +0000241}