OLD | NEW |
1 /* | 1 /* |
2 * Copyright 2013 Google Inc. | 2 * Copyright 2013 Google Inc. |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license that can be | 4 * Use of this source code is governed by a BSD-style license that can be |
5 * found in the LICENSE file. | 5 * found in the LICENSE file. |
6 */ | 6 */ |
7 | 7 |
| 8 #include "SkDiscardableMemory.h" |
8 #include "SkDiscardableMemoryPool.h" | 9 #include "SkDiscardableMemoryPool.h" |
9 #include "SkOnce.h" | 10 #include "SkOnce.h" |
| 11 #include "SkTInternalLList.h" |
| 12 #include "SkThread.h" |
10 | 13 |
11 // Note: | 14 // Note: |
12 // A PoolDiscardableMemory is memory that is counted in a pool. | 15 // A PoolDiscardableMemory is memory that is counted in a pool. |
13 // A DiscardableMemoryPool is a pool of PoolDiscardableMemorys. | 16 // A DiscardableMemoryPool is a pool of PoolDiscardableMemorys. |
14 | 17 |
| 18 namespace { |
| 19 |
| 20 class PoolDiscardableMemory; |
| 21 |
15 /** | 22 /** |
16 * A SkPoolDiscardableMemory is a SkDiscardableMemory that relies on | 23 * This non-global pool can be used for unit tests to verify that the |
17 * a SkDiscardableMemoryPool object to manage the memory. | 24 * pool works. |
18 */ | 25 */ |
19 class SkPoolDiscardableMemory : public SkDiscardableMemory { | 26 class DiscardableMemoryPool : public SkDiscardableMemoryPool { |
20 public: | 27 public: |
21 SkPoolDiscardableMemory(SkDiscardableMemoryPool* pool, | 28 /** |
| 29 * Without mutex, will be not be thread safe. |
| 30 */ |
| 31 DiscardableMemoryPool(size_t budget, SkBaseMutex* mutex = NULL); |
| 32 virtual ~DiscardableMemoryPool(); |
| 33 |
| 34 virtual SkDiscardableMemory* create(size_t bytes) SK_OVERRIDE; |
| 35 |
| 36 virtual size_t getRAMUsed() SK_OVERRIDE; |
| 37 virtual void setRAMBudget(size_t budget) SK_OVERRIDE; |
| 38 virtual size_t getRAMBudget() SK_OVERRIDE { return fBudget; } |
| 39 |
| 40 /** purges all unlocked DMs */ |
| 41 virtual void dumpPool() SK_OVERRIDE; |
| 42 |
| 43 #if SK_LAZY_CACHE_STATS // Defined in SkDiscardableMemoryPool.h |
| 44 virtual int getCacheHits() SK_OVERRIDE { return fCacheHits; } |
| 45 virtual int getCacheMisses() SK_OVERRIDE { return fCacheMisses; } |
| 46 virtual void resetCacheHitsAndMisses() SK_OVERRIDE { |
| 47 fCacheHits = fCacheMisses = 0; |
| 48 } |
| 49 int fCacheHits; |
| 50 int fCacheMisses; |
| 51 #endif // SK_LAZY_CACHE_STATS |
| 52 |
| 53 private: |
| 54 SkBaseMutex* fMutex; |
| 55 size_t fBudget; |
| 56 size_t fUsed; |
| 57 SkTInternalLList<PoolDiscardableMemory> fList; |
| 58 |
| 59 /** Function called to free memory if needed */ |
| 60 void dumpDownTo(size_t budget); |
| 61 /** called by DiscardableMemoryPool upon destruction */ |
| 62 void free(PoolDiscardableMemory* dm); |
| 63 /** called by DiscardableMemoryPool::lock() */ |
| 64 bool lock(PoolDiscardableMemory* dm); |
| 65 /** called by DiscardableMemoryPool::unlock() */ |
| 66 void unlock(PoolDiscardableMemory* dm); |
| 67 |
| 68 friend class PoolDiscardableMemory; |
| 69 |
| 70 typedef SkDiscardableMemory::Factory INHERITED; |
| 71 }; |
| 72 |
| 73 /** |
| 74 * A PoolDiscardableMemory is a SkDiscardableMemory that relies on |
| 75 * a DiscardableMemoryPool object to manage the memory. |
| 76 */ |
| 77 class PoolDiscardableMemory : public SkDiscardableMemory { |
| 78 public: |
| 79 PoolDiscardableMemory(DiscardableMemoryPool* pool, |
22 void* pointer, size_t bytes); | 80 void* pointer, size_t bytes); |
23 virtual ~SkPoolDiscardableMemory(); | 81 virtual ~PoolDiscardableMemory(); |
24 virtual bool lock() SK_OVERRIDE; | 82 virtual bool lock() SK_OVERRIDE; |
25 virtual void* data() SK_OVERRIDE; | 83 virtual void* data() SK_OVERRIDE; |
26 virtual void unlock() SK_OVERRIDE; | 84 virtual void unlock() SK_OVERRIDE; |
27 friend class SkDiscardableMemoryPool; | 85 friend class DiscardableMemoryPool; |
28 private: | 86 private: |
29 SK_DECLARE_INTERNAL_LLIST_INTERFACE(SkPoolDiscardableMemory); | 87 SK_DECLARE_INTERNAL_LLIST_INTERFACE(PoolDiscardableMemory); |
30 SkDiscardableMemoryPool* const fPool; | 88 DiscardableMemoryPool* const fPool; |
31 bool fLocked; | 89 bool fLocked; |
32 void* fPointer; | 90 void* fPointer; |
33 const size_t fBytes; | 91 const size_t fBytes; |
34 }; | 92 }; |
35 | 93 |
36 SkPoolDiscardableMemory::SkPoolDiscardableMemory(SkDiscardableMemoryPool* pool, | 94 PoolDiscardableMemory::PoolDiscardableMemory(DiscardableMemoryPool* pool, |
37 void* pointer, | 95 void* pointer, |
38 size_t bytes) | 96 size_t bytes) |
39 : fPool(pool) | 97 : fPool(pool) |
40 , fLocked(true) | 98 , fLocked(true) |
41 , fPointer(pointer) | 99 , fPointer(pointer) |
42 , fBytes(bytes) { | 100 , fBytes(bytes) { |
43 SkASSERT(fPool != NULL); | 101 SkASSERT(fPool != NULL); |
44 SkASSERT(fPointer != NULL); | 102 SkASSERT(fPointer != NULL); |
45 SkASSERT(fBytes > 0); | 103 SkASSERT(fBytes > 0); |
46 fPool->ref(); | 104 fPool->ref(); |
47 } | 105 } |
48 | 106 |
49 SkPoolDiscardableMemory::~SkPoolDiscardableMemory() { | 107 PoolDiscardableMemory::~PoolDiscardableMemory() { |
50 SkASSERT(!fLocked); // contract for SkDiscardableMemory | 108 SkASSERT(!fLocked); // contract for SkDiscardableMemory |
51 fPool->free(this); | 109 fPool->free(this); |
52 fPool->unref(); | 110 fPool->unref(); |
53 } | 111 } |
54 | 112 |
55 bool SkPoolDiscardableMemory::lock() { | 113 bool PoolDiscardableMemory::lock() { |
56 SkASSERT(!fLocked); // contract for SkDiscardableMemory | 114 SkASSERT(!fLocked); // contract for SkDiscardableMemory |
57 return fPool->lock(this); | 115 return fPool->lock(this); |
58 } | 116 } |
59 | 117 |
60 void* SkPoolDiscardableMemory::data() { | 118 void* PoolDiscardableMemory::data() { |
61 SkASSERT(fLocked); // contract for SkDiscardableMemory | 119 SkASSERT(fLocked); // contract for SkDiscardableMemory |
62 return fPointer; | 120 return fPointer; |
63 } | 121 } |
64 | 122 |
65 void SkPoolDiscardableMemory::unlock() { | 123 void PoolDiscardableMemory::unlock() { |
66 SkASSERT(fLocked); // contract for SkDiscardableMemory | 124 SkASSERT(fLocked); // contract for SkDiscardableMemory |
67 fPool->unlock(this); | 125 fPool->unlock(this); |
68 } | 126 } |
69 | 127 |
70 //////////////////////////////////////////////////////////////////////////////// | 128 //////////////////////////////////////////////////////////////////////////////// |
71 | 129 |
72 SkDiscardableMemoryPool::SkDiscardableMemoryPool(size_t budget, | 130 DiscardableMemoryPool::DiscardableMemoryPool(size_t budget, |
73 SkBaseMutex* mutex) | 131 SkBaseMutex* mutex) |
74 : fMutex(mutex) | 132 : fMutex(mutex) |
75 , fBudget(budget) | 133 , fBudget(budget) |
76 , fUsed(0) { | 134 , fUsed(0) { |
77 #if LAZY_CACHE_STATS | 135 #if SK_LAZY_CACHE_STATS |
78 fCacheHits = 0; | 136 fCacheHits = 0; |
79 fCacheMisses = 0; | 137 fCacheMisses = 0; |
80 #endif // LAZY_CACHE_STATS | 138 #endif // SK_LAZY_CACHE_STATS |
81 } | 139 } |
82 SkDiscardableMemoryPool::~SkDiscardableMemoryPool() { | 140 DiscardableMemoryPool::~DiscardableMemoryPool() { |
83 // SkPoolDiscardableMemory objects that belong to this pool are | 141 // PoolDiscardableMemory objects that belong to this pool are |
84 // always deleted before deleting this pool since each one has a | 142 // always deleted before deleting this pool since each one has a |
85 // ref to the pool. | 143 // ref to the pool. |
86 SkASSERT(fList.isEmpty()); | 144 SkASSERT(fList.isEmpty()); |
87 } | 145 } |
88 | 146 |
89 void SkDiscardableMemoryPool::dumpDownTo(size_t budget) { | 147 void DiscardableMemoryPool::dumpDownTo(size_t budget) { |
90 // assert((NULL = fMutex) || fMutex->isLocked()); | 148 // assert((NULL = fMutex) || fMutex->isLocked()); |
91 // TODO(halcanary) implement bool fMutex::isLocked(). | 149 // TODO(halcanary) implement bool fMutex::isLocked(). |
92 // WARNING: only call this function after aquiring lock. | 150 // WARNING: only call this function after aquiring lock. |
93 if (fUsed <= budget) { | 151 if (fUsed <= budget) { |
94 return; | 152 return; |
95 } | 153 } |
96 typedef SkTInternalLList<SkPoolDiscardableMemory>::Iter Iter; | 154 typedef SkTInternalLList<PoolDiscardableMemory>::Iter Iter; |
97 Iter iter; | 155 Iter iter; |
98 SkPoolDiscardableMemory* cur = iter.init(fList, Iter::kTail_IterStart); | 156 PoolDiscardableMemory* cur = iter.init(fList, Iter::kTail_IterStart); |
99 while ((fUsed > budget) && (NULL != cur)) { | 157 while ((fUsed > budget) && (NULL != cur)) { |
100 if (!cur->fLocked) { | 158 if (!cur->fLocked) { |
101 SkPoolDiscardableMemory* dm = cur; | 159 PoolDiscardableMemory* dm = cur; |
102 SkASSERT(dm->fPointer != NULL); | 160 SkASSERT(dm->fPointer != NULL); |
103 sk_free(dm->fPointer); | 161 sk_free(dm->fPointer); |
104 dm->fPointer = NULL; | 162 dm->fPointer = NULL; |
105 SkASSERT(fUsed >= dm->fBytes); | 163 SkASSERT(fUsed >= dm->fBytes); |
106 fUsed -= dm->fBytes; | 164 fUsed -= dm->fBytes; |
107 cur = iter.prev(); | 165 cur = iter.prev(); |
108 // Purged DMs are taken out of the list. This saves times | 166 // Purged DMs are taken out of the list. This saves times |
109 // looking them up. Purged DMs are NOT deleted. | 167 // looking them up. Purged DMs are NOT deleted. |
110 fList.remove(dm); | 168 fList.remove(dm); |
111 } else { | 169 } else { |
112 cur = iter.prev(); | 170 cur = iter.prev(); |
113 } | 171 } |
114 } | 172 } |
115 } | 173 } |
116 | 174 |
117 SkDiscardableMemory* SkDiscardableMemoryPool::create(size_t bytes) { | 175 SkDiscardableMemory* DiscardableMemoryPool::create(size_t bytes) { |
118 void* addr = sk_malloc_flags(bytes, 0); | 176 void* addr = sk_malloc_flags(bytes, 0); |
119 if (NULL == addr) { | 177 if (NULL == addr) { |
120 return NULL; | 178 return NULL; |
121 } | 179 } |
122 SkPoolDiscardableMemory* dm = SkNEW_ARGS(SkPoolDiscardableMemory, | 180 PoolDiscardableMemory* dm = SkNEW_ARGS(PoolDiscardableMemory, |
123 (this, addr, bytes)); | 181 (this, addr, bytes)); |
124 SkAutoMutexAcquire autoMutexAcquire(fMutex); | 182 SkAutoMutexAcquire autoMutexAcquire(fMutex); |
125 fList.addToHead(dm); | 183 fList.addToHead(dm); |
126 fUsed += bytes; | 184 fUsed += bytes; |
127 this->dumpDownTo(fBudget); | 185 this->dumpDownTo(fBudget); |
128 return dm; | 186 return dm; |
129 } | 187 } |
130 | 188 |
131 void SkDiscardableMemoryPool::free(SkPoolDiscardableMemory* dm) { | 189 void DiscardableMemoryPool::free(PoolDiscardableMemory* dm) { |
132 // This is called by dm's destructor. | 190 // This is called by dm's destructor. |
133 if (dm->fPointer != NULL) { | 191 if (dm->fPointer != NULL) { |
134 SkAutoMutexAcquire autoMutexAcquire(fMutex); | 192 SkAutoMutexAcquire autoMutexAcquire(fMutex); |
135 sk_free(dm->fPointer); | 193 sk_free(dm->fPointer); |
136 dm->fPointer = NULL; | 194 dm->fPointer = NULL; |
137 SkASSERT(fUsed >= dm->fBytes); | 195 SkASSERT(fUsed >= dm->fBytes); |
138 fUsed -= dm->fBytes; | 196 fUsed -= dm->fBytes; |
139 fList.remove(dm); | 197 fList.remove(dm); |
140 } else { | 198 } else { |
141 SkASSERT(!fList.isInList(dm)); | 199 SkASSERT(!fList.isInList(dm)); |
142 } | 200 } |
143 } | 201 } |
144 | 202 |
145 bool SkDiscardableMemoryPool::lock(SkPoolDiscardableMemory* dm) { | 203 bool DiscardableMemoryPool::lock(PoolDiscardableMemory* dm) { |
146 SkASSERT(dm != NULL); | 204 SkASSERT(dm != NULL); |
147 if (NULL == dm->fPointer) { | 205 if (NULL == dm->fPointer) { |
148 #if LAZY_CACHE_STATS | 206 #if SK_LAZY_CACHE_STATS |
149 SkAutoMutexAcquire autoMutexAcquire(fMutex); | 207 SkAutoMutexAcquire autoMutexAcquire(fMutex); |
150 ++fCacheMisses; | 208 ++fCacheMisses; |
151 #endif // LAZY_CACHE_STATS | 209 #endif // SK_LAZY_CACHE_STATS |
152 return false; | 210 return false; |
153 } | 211 } |
154 SkAutoMutexAcquire autoMutexAcquire(fMutex); | 212 SkAutoMutexAcquire autoMutexAcquire(fMutex); |
155 if (NULL == dm->fPointer) { | 213 if (NULL == dm->fPointer) { |
156 // May have been purged while waiting for lock. | 214 // May have been purged while waiting for lock. |
157 #if LAZY_CACHE_STATS | 215 #if SK_LAZY_CACHE_STATS |
158 ++fCacheMisses; | 216 ++fCacheMisses; |
159 #endif // LAZY_CACHE_STATS | 217 #endif // SK_LAZY_CACHE_STATS |
160 return false; | 218 return false; |
161 } | 219 } |
162 dm->fLocked = true; | 220 dm->fLocked = true; |
163 fList.remove(dm); | 221 fList.remove(dm); |
164 fList.addToHead(dm); | 222 fList.addToHead(dm); |
165 #if LAZY_CACHE_STATS | 223 #if SK_LAZY_CACHE_STATS |
166 ++fCacheHits; | 224 ++fCacheHits; |
167 #endif // LAZY_CACHE_STATS | 225 #endif // SK_LAZY_CACHE_STATS |
168 return true; | 226 return true; |
169 } | 227 } |
170 | 228 |
171 void SkDiscardableMemoryPool::unlock(SkPoolDiscardableMemory* dm) { | 229 void DiscardableMemoryPool::unlock(PoolDiscardableMemory* dm) { |
172 SkASSERT(dm != NULL); | 230 SkASSERT(dm != NULL); |
173 SkAutoMutexAcquire autoMutexAcquire(fMutex); | 231 SkAutoMutexAcquire autoMutexAcquire(fMutex); |
174 dm->fLocked = false; | 232 dm->fLocked = false; |
175 this->dumpDownTo(fBudget); | 233 this->dumpDownTo(fBudget); |
176 } | 234 } |
177 | 235 |
178 size_t SkDiscardableMemoryPool::getRAMUsed() { | 236 size_t DiscardableMemoryPool::getRAMUsed() { |
179 return fUsed; | 237 return fUsed; |
180 } | 238 } |
181 void SkDiscardableMemoryPool::setRAMBudget(size_t budget) { | 239 void DiscardableMemoryPool::setRAMBudget(size_t budget) { |
182 SkAutoMutexAcquire autoMutexAcquire(fMutex); | 240 SkAutoMutexAcquire autoMutexAcquire(fMutex); |
183 fBudget = budget; | 241 fBudget = budget; |
184 this->dumpDownTo(fBudget); | 242 this->dumpDownTo(fBudget); |
185 } | 243 } |
186 void SkDiscardableMemoryPool::dumpPool() { | 244 void DiscardableMemoryPool::dumpPool() { |
187 SkAutoMutexAcquire autoMutexAcquire(fMutex); | 245 SkAutoMutexAcquire autoMutexAcquire(fMutex); |
188 this->dumpDownTo(0); | 246 this->dumpDownTo(0); |
189 } | 247 } |
190 | 248 |
191 //////////////////////////////////////////////////////////////////////////////// | 249 //////////////////////////////////////////////////////////////////////////////// |
192 SK_DECLARE_STATIC_MUTEX(gMutex); | 250 SK_DECLARE_STATIC_MUTEX(gMutex); |
193 static void create_pool(SkDiscardableMemoryPool** pool) { | 251 SkDiscardableMemoryPool* gPool = NULL; |
194 SkASSERT(NULL == *pool); | 252 void create_global_pool(int) { |
195 *pool = SkNEW_ARGS(SkDiscardableMemoryPool, | 253 SkASSERT(NULL == gPool); |
196 (SK_DEFAULT_GLOBAL_DISCARDABLE_MEMORY_POOL_SIZE, | 254 gPool = SkDiscardableMemoryPool::Create( |
197 &gMutex)); | 255 SK_DEFAULT_GLOBAL_DISCARDABLE_MEMORY_POOL_SIZE, &gMutex); |
198 } | 256 } |
| 257 void cleanup_global_pool() { |
| 258 gPool->unref(); |
| 259 } |
| 260 } // namespace |
| 261 |
| 262 SkDiscardableMemoryPool* SkDiscardableMemoryPool::Create( |
| 263 size_t size, SkBaseMutex* mutex) { |
| 264 return SkNEW_ARGS(DiscardableMemoryPool, (size, mutex)); |
| 265 } |
| 266 |
199 SkDiscardableMemoryPool* SkGetGlobalDiscardableMemoryPool() { | 267 SkDiscardableMemoryPool* SkGetGlobalDiscardableMemoryPool() { |
200 static SkDiscardableMemoryPool* gPool(NULL); | |
201 SK_DECLARE_STATIC_ONCE(create_pool_once); | 268 SK_DECLARE_STATIC_ONCE(create_pool_once); |
202 SkOnce(&create_pool_once, create_pool, &gPool); | 269 SkOnce(&create_pool_once, create_global_pool, 0, &cleanup_global_pool); |
203 SkASSERT(NULL != gPool); | 270 SkASSERT(NULL != gPool); |
204 return gPool; | 271 return gPool; |
205 } | 272 } |
206 | 273 |
207 //////////////////////////////////////////////////////////////////////////////// | 274 //////////////////////////////////////////////////////////////////////////////// |
OLD | NEW |