blob: a1b2438a2bcc79a9c80eb3c49c7afc281cda112b [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"
9#include "SkOnce.h"
10
11// Note:
12// A PoolDiscardableMemory is memory that is counted in a pool.
13// A DiscardableMemoryPool is a pool of PoolDiscardableMemorys.
14
15/**
16 * A SkPoolDiscardableMemory is a SkDiscardableMemory that relies on
17 * a SkDiscardableMemoryPool object to manage the memory.
18 */
19class SkPoolDiscardableMemory : public SkDiscardableMemory {
20public:
21 SkPoolDiscardableMemory(SkDiscardableMemoryPool* pool,
22 void* pointer, size_t bytes);
23 virtual ~SkPoolDiscardableMemory();
24 virtual bool lock() SK_OVERRIDE;
25 virtual void* data() SK_OVERRIDE;
26 virtual void unlock() SK_OVERRIDE;
27 friend class SkDiscardableMemoryPool;
28private:
29 SK_DECLARE_INTERNAL_LLIST_INTERFACE(SkPoolDiscardableMemory);
30 SkDiscardableMemoryPool* const fPool;
31 bool fLocked;
32 void* fPointer;
33 const size_t fBytes;
34};
35
36SkPoolDiscardableMemory::SkPoolDiscardableMemory(SkDiscardableMemoryPool* pool,
37 void* pointer,
38 size_t bytes)
39 : fPool(pool)
40 , fLocked(true)
41 , fPointer(pointer)
42 , fBytes(bytes) {
43 SkASSERT(fPool != NULL);
44 SkASSERT(fPointer != NULL);
45 SkASSERT(fBytes > 0);
46 fPool->ref();
47}
48
49SkPoolDiscardableMemory::~SkPoolDiscardableMemory() {
50 fPool->free(this);
51 fPool->unref();
52}
53
54bool SkPoolDiscardableMemory::lock() {
55 return fPool->lock(this);
56}
57
58void* SkPoolDiscardableMemory::data() {
59 return fLocked ? fPointer : NULL;
60}
61
62void SkPoolDiscardableMemory::unlock() {
63 fPool->unlock(this);
64}
65
66////////////////////////////////////////////////////////////////////////////////
67
68SkDiscardableMemoryPool::SkDiscardableMemoryPool(size_t budget,
69 SkBaseMutex* mutex)
70 : fMutex(mutex)
71 , fBudget(budget)
72 , fUsed(0) {
73 #if LAZY_CACHE_STATS
74 fCacheHits = 0;
75 fCacheMisses = 0;
76 #endif // LAZY_CACHE_STATS
77}
78SkDiscardableMemoryPool::~SkDiscardableMemoryPool() {
79 // SkPoolDiscardableMemory objects that belong to this pool are
80 // always deleted before deleting this pool since each one has a
81 // ref to the pool.
82 SkASSERT(fList.isEmpty());
83}
84
85void SkDiscardableMemoryPool::dumpDownTo(size_t budget) {
86 // assert((NULL = fMutex) || fMutex->isLocked());
87 // TODO(halcanary) implement bool fMutex::isLocked().
88 // WARNING: only call this function after aquiring lock.
89 if (fUsed <= budget) {
90 return;
91 }
92 typedef SkTInternalLList<SkPoolDiscardableMemory>::Iter Iter;
93 Iter iter;
94 SkPoolDiscardableMemory* cur = iter.init(fList, Iter::kTail_IterStart);
95 while ((fUsed > budget) && (NULL != cur)) {
96 if (!cur->fLocked) {
97 SkPoolDiscardableMemory* dm = cur;
98 SkASSERT(dm->fPointer != NULL);
99 sk_free(dm->fPointer);
100 dm->fPointer = NULL;
101 SkASSERT(fUsed >= dm->fBytes);
102 fUsed -= dm->fBytes;
103 cur = iter.prev();
104 // Purged DMs are taken out of the list. This saves times
105 // looking them up. Purged DMs are NOT deleted.
106 fList.remove(dm);
107 } else {
108 cur = iter.prev();
109 }
110 }
111}
112
113SkDiscardableMemory* SkDiscardableMemoryPool::create(size_t bytes) {
114 void* addr = sk_malloc_flags(bytes, 0);
115 if (NULL == addr) {
116 return NULL;
117 }
118 SkPoolDiscardableMemory* dm = SkNEW_ARGS(SkPoolDiscardableMemory,
119 (this, addr, bytes));
120 SkAutoMutexAcquire autoMutexAcquire(fMutex);
121 fList.addToHead(dm);
122 fUsed += bytes;
123 this->dumpDownTo(fBudget);
124 return dm;
125}
126
127void SkDiscardableMemoryPool::free(SkPoolDiscardableMemory* dm) {
128 // This is called by dm's destructor.
129 if (dm->fPointer != NULL) {
130 SkAutoMutexAcquire autoMutexAcquire(fMutex);
131 sk_free(dm->fPointer);
132 dm->fPointer = NULL;
133 SkASSERT(fUsed >= dm->fBytes);
134 fUsed -= dm->fBytes;
135 fList.remove(dm);
136 } else {
137 SkASSERT(!fList.isInList(dm));
138 }
139}
140
141bool SkDiscardableMemoryPool::lock(SkPoolDiscardableMemory* dm) {
142 SkASSERT(dm != NULL);
143 if (NULL == dm->fPointer) {
144 #if LAZY_CACHE_STATS
145 SkAutoMutexAcquire autoMutexAcquire(fMutex);
146 ++fCacheMisses;
147 #endif // LAZY_CACHE_STATS
148 return false;
149 }
150 SkAutoMutexAcquire autoMutexAcquire(fMutex);
151 if (NULL == dm->fPointer) {
152 // May have been purged while waiting for lock.
153 #if LAZY_CACHE_STATS
154 ++fCacheMisses;
155 #endif // LAZY_CACHE_STATS
156 return false;
157 }
158 dm->fLocked = true;
159 fList.remove(dm);
160 fList.addToHead(dm);
161 #if LAZY_CACHE_STATS
162 ++fCacheHits;
163 #endif // LAZY_CACHE_STATS
164 return true;
165}
166
167void SkDiscardableMemoryPool::unlock(SkPoolDiscardableMemory* dm) {
168 SkASSERT(dm != NULL);
169 SkAutoMutexAcquire autoMutexAcquire(fMutex);
170 dm->fLocked = false;
171 this->dumpDownTo(fBudget);
172}
173
174size_t SkDiscardableMemoryPool::getRAMUsed() {
175 return fUsed;
176}
177void SkDiscardableMemoryPool::setRAMBudget(size_t budget) {
178 SkAutoMutexAcquire autoMutexAcquire(fMutex);
179 fBudget = budget;
180 this->dumpDownTo(fBudget);
181}
182void SkDiscardableMemoryPool::dumpPool() {
183 SkAutoMutexAcquire autoMutexAcquire(fMutex);
184 this->dumpDownTo(0);
185}
186
187////////////////////////////////////////////////////////////////////////////////
188SK_DECLARE_STATIC_MUTEX(gMutex);
189static void create_pool(SkDiscardableMemoryPool** pool) {
190 SkASSERT(NULL == *pool);
191 *pool = SkNEW_ARGS(SkDiscardableMemoryPool,
192 (SK_DEFAULT_GLOBAL_DISCARDABLE_MEMORY_POOL_SIZE,
193 &gMutex));
194}
195SkDiscardableMemoryPool* SkGetGlobalDiscardableMemoryPool() {
196 static SkDiscardableMemoryPool* gPool(NULL);
197 SK_DECLARE_STATIC_ONCE(create_pool_once);
198 SkOnce(&create_pool_once, create_pool, &gPool);
199 SkASSERT(NULL != gPool);
200 return gPool;
201}
202
203////////////////////////////////////////////////////////////////////////////////