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