OLD | NEW |
---|---|
(Empty) | |
1 /* | |
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 "SkThread.h" | |
10 #include "SkOnce.h" | |
11 | |
12 // Note: | |
13 // A PoolDiscardableMemory is memory that is counted in a pool. | |
14 // A DiscardableMemoryPool is a pool of PoolDiscardableMemorys. | |
15 | |
16 /** | |
17 * A SkPoolDiscardableMemory is a SkDiscardableMemory that relies on | |
18 * a SkDiscardableMemoryPool object to manage the memory. | |
19 */ | |
20 class SkPoolDiscardableMemory : public SkDiscardableMemory { | |
21 public: | |
22 SkPoolDiscardableMemory(SkDiscardableMemoryPool* pool, | |
23 void* pointer, size_t bytes); | |
24 virtual ~SkPoolDiscardableMemory(); | |
25 virtual bool lock() SK_OVERRIDE; | |
26 virtual void* data() SK_OVERRIDE; | |
27 virtual void unlock() SK_OVERRIDE; | |
28 friend class SkDiscardableMemoryPool; | |
29 private: | |
30 SK_DECLARE_INTERNAL_LLIST_INTERFACE(SkPoolDiscardableMemory); | |
31 SkDiscardableMemoryPool* const fPool; | |
32 bool fLocked; | |
33 void* fPointer; | |
34 const size_t fBytes; | |
35 }; | |
36 | |
37 SkPoolDiscardableMemory::SkPoolDiscardableMemory(SkDiscardableMemoryPool* pool, | |
38 void* pointer, | |
39 size_t bytes) | |
40 : fPool(pool) | |
41 , fLocked(true) | |
42 , fPointer(pointer) | |
43 , fBytes(bytes) { | |
44 SkASSERT(fPool != NULL); | |
45 SkASSERT(fPointer != NULL); | |
46 SkASSERT(fBytes > 0); | |
47 fPool->ref(); | |
48 } | |
49 | |
50 SkPoolDiscardableMemory::~SkPoolDiscardableMemory() { | |
51 fPool->free(this); | |
52 fPool->unref(); | |
53 } | |
54 | |
55 bool SkPoolDiscardableMemory::lock() { | |
56 return fPool->lock(this); | |
57 } | |
58 | |
59 void* SkPoolDiscardableMemory::data() { | |
60 return fLocked ? fPointer : NULL; | |
61 } | |
62 | |
63 void SkPoolDiscardableMemory::unlock() { | |
64 fPool->unlock(this); | |
65 } | |
66 | |
67 //////////////////////////////////////////////////////////////////////////////// | |
68 | |
69 SkDiscardableMemoryPool::SkDiscardableMemoryPool(size_t budget, | |
70 SkBaseMutex* mutex) | |
71 : fMutex(mutex) | |
72 , fBudget(budget) | |
73 , fUsed(0) { | |
74 #if LAZY_CACHE_STATS | |
75 fCacheHits = 0; | |
76 fCacheMisses = 0; | |
77 #endif // LAZY_CACHE_STATS | |
78 } | |
79 SkDiscardableMemoryPool::~SkDiscardableMemoryPool() { | |
80 // Please delete the SkPoolDiscardableMemory objects that belong | |
81 // to this pool before deleting this pool. | |
82 SkASSERT(fList.isEmpty()); | |
83 } | |
84 | |
85 void 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 (budget <= fUsed) { | |
90 return; | |
91 } | |
92 typedef SkTInternalLList<SkPoolDiscardableMemory>::Iter Iter; | |
93 Iter iter; | |
94 SkPoolDiscardableMemory* cur = iter.init(fList, Iter::kTail_IterStart); | |
95 while ((budget > fUsed) && (NULL != cur)) { | |
96 if ((!cur->fLocked) && (cur->fPointer != NULL)) { | |
scroggo
2013/12/03 23:00:01
Will curr->fPointer ever be NULL? I would think no
hal.canary
2013/12/04 00:01:36
Done.
| |
97 SkPoolDiscardableMemory* dm = cur; | |
98 SkASSERT(dm->fPointer != NULL); | |
99 sk_free(dm->fPointer); | |
scroggo
2013/12/03 23:00:01
Should this function call this->free(dm)?
hal.canary
2013/12/04 00:01:36
No. We have the mutex, so we can't directly call
| |
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 | |
113 SkDiscardableMemory* 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 | |
127 void 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 } | |
141 | |
142 bool SkDiscardableMemoryPool::lock(SkPoolDiscardableMemory* dm) { | |
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 | |
167 void SkDiscardableMemoryPool::unlock(SkPoolDiscardableMemory* dm) { | |
168 SkAutoMutexAcquire autoMutexAcquire(fMutex); | |
169 dm->fLocked = false; | |
170 this->dumpDownTo(fBudget); | |
171 } | |
172 | |
173 size_t SkDiscardableMemoryPool::getRAMUsed() { | |
174 return fUsed; | |
175 } | |
176 void SkDiscardableMemoryPool::setRAMBudget(size_t budget) { | |
177 SkAutoMutexAcquire autoMutexAcquire(fMutex); | |
178 fBudget = budget; | |
179 this->dumpDownTo(fBudget); | |
180 } | |
181 void SkDiscardableMemoryPool::dumpPool() { | |
182 SkAutoMutexAcquire autoMutexAcquire(fMutex); | |
183 this->dumpDownTo(0); | |
184 } | |
185 | |
186 //////////////////////////////////////////////////////////////////////////////// | |
187 SK_DECLARE_STATIC_MUTEX(gMutex); | |
188 static void create_pool(SkDiscardableMemoryPool** pool) { | |
189 SkASSERT(NULL == *pool); | |
190 *pool = SkNEW_ARGS(SkDiscardableMemoryPool, | |
191 (SK_DEFAULT_GLOBAL_DISCARDABLE_MEMORY_POOL_SIZE, | |
192 &gMutex)); | |
193 } | |
194 SkDiscardableMemoryPool* SkGetGlobalDiscardableMemoryPool() { | |
195 static SkDiscardableMemoryPool* gPool(NULL); | |
196 SK_DECLARE_STATIC_ONCE(create_pool_once); | |
197 SkOnce(&create_pool_once, create_pool, &gPool); | |
198 SkASSERT(NULL != gPool); | |
199 return gPool; | |
200 } | |
201 | |
202 //////////////////////////////////////////////////////////////////////////////// | |
OLD | NEW |