Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / third_party / skia / src / lazy / SkDiscardableMemoryPool.cpp
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 "SkDiscardableMemory.h"
9 #include "SkDiscardableMemoryPool.h"
10 #include "SkOnce.h"
11 #include "SkTInternalLList.h"
12 #include "SkThread.h"
13
14 // Note:
15 // A PoolDiscardableMemory is memory that is counted in a pool.
16 // A DiscardableMemoryPool is a pool of PoolDiscardableMemorys.
17
18 namespace {
19
20 class PoolDiscardableMemory;
21
22 /**
23  *  This non-global pool can be used for unit tests to verify that the
24  *  pool works.
25  */
26 class DiscardableMemoryPool : public SkDiscardableMemoryPool {
27 public:
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,
80                             void* pointer, size_t bytes);
81     virtual ~PoolDiscardableMemory();
82     virtual bool lock() SK_OVERRIDE;
83     virtual void* data() SK_OVERRIDE;
84     virtual void unlock() SK_OVERRIDE;
85     friend class DiscardableMemoryPool;
86 private:
87     SK_DECLARE_INTERNAL_LLIST_INTERFACE(PoolDiscardableMemory);
88     DiscardableMemoryPool* const fPool;
89     bool                         fLocked;
90     void*                        fPointer;
91     const size_t                 fBytes;
92 };
93
94 PoolDiscardableMemory::PoolDiscardableMemory(DiscardableMemoryPool* pool,
95                                              void* pointer,
96                                              size_t bytes)
97     : fPool(pool)
98     , fLocked(true)
99     , fPointer(pointer)
100     , fBytes(bytes) {
101     SkASSERT(fPool != NULL);
102     SkASSERT(fPointer != NULL);
103     SkASSERT(fBytes > 0);
104     fPool->ref();
105 }
106
107 PoolDiscardableMemory::~PoolDiscardableMemory() {
108     SkASSERT(!fLocked); // contract for SkDiscardableMemory
109     fPool->free(this);
110     fPool->unref();
111 }
112
113 bool PoolDiscardableMemory::lock() {
114     SkASSERT(!fLocked); // contract for SkDiscardableMemory
115     return fPool->lock(this);
116 }
117
118 void* PoolDiscardableMemory::data() {
119     SkASSERT(fLocked); // contract for SkDiscardableMemory
120     return fPointer;
121 }
122
123 void PoolDiscardableMemory::unlock() {
124     SkASSERT(fLocked); // contract for SkDiscardableMemory
125     fPool->unlock(this);
126 }
127
128 ////////////////////////////////////////////////////////////////////////////////
129
130 DiscardableMemoryPool::DiscardableMemoryPool(size_t budget,
131                                              SkBaseMutex* mutex)
132     : fMutex(mutex)
133     , fBudget(budget)
134     , fUsed(0) {
135     #if SK_LAZY_CACHE_STATS
136     fCacheHits = 0;
137     fCacheMisses = 0;
138     #endif  // SK_LAZY_CACHE_STATS
139 }
140 DiscardableMemoryPool::~DiscardableMemoryPool() {
141     // PoolDiscardableMemory objects that belong to this pool are
142     // always deleted before deleting this pool since each one has a
143     // ref to the pool.
144     SkASSERT(fList.isEmpty());
145 }
146
147 void DiscardableMemoryPool::dumpDownTo(size_t budget) {
148     // assert((NULL = fMutex) || fMutex->isLocked());
149     // TODO(halcanary) implement bool fMutex::isLocked().
150     // WARNING: only call this function after aquiring lock.
151     if (fUsed <= budget) {
152         return;
153     }
154     typedef SkTInternalLList<PoolDiscardableMemory>::Iter Iter;
155     Iter iter;
156     PoolDiscardableMemory* cur = iter.init(fList, Iter::kTail_IterStart);
157     while ((fUsed > budget) && (NULL != cur)) {
158         if (!cur->fLocked) {
159             PoolDiscardableMemory* dm = cur;
160             SkASSERT(dm->fPointer != NULL);
161             sk_free(dm->fPointer);
162             dm->fPointer = NULL;
163             SkASSERT(fUsed >= dm->fBytes);
164             fUsed -= dm->fBytes;
165             cur = iter.prev();
166             // Purged DMs are taken out of the list.  This saves times
167             // looking them up.  Purged DMs are NOT deleted.
168             fList.remove(dm);
169         } else {
170             cur = iter.prev();
171         }
172     }
173 }
174
175 SkDiscardableMemory* DiscardableMemoryPool::create(size_t bytes) {
176     void* addr = sk_malloc_flags(bytes, 0);
177     if (NULL == addr) {
178         return NULL;
179     }
180     PoolDiscardableMemory* dm = SkNEW_ARGS(PoolDiscardableMemory,
181                                              (this, addr, bytes));
182     SkAutoMutexAcquire autoMutexAcquire(fMutex);
183     fList.addToHead(dm);
184     fUsed += bytes;
185     this->dumpDownTo(fBudget);
186     return dm;
187 }
188
189 void DiscardableMemoryPool::free(PoolDiscardableMemory* dm) {
190     // This is called by dm's destructor.
191     if (dm->fPointer != NULL) {
192         SkAutoMutexAcquire autoMutexAcquire(fMutex);
193         sk_free(dm->fPointer);
194         dm->fPointer = NULL;
195         SkASSERT(fUsed >= dm->fBytes);
196         fUsed -= dm->fBytes;
197         fList.remove(dm);
198     } else {
199         SkASSERT(!fList.isInList(dm));
200     }
201 }
202
203 bool DiscardableMemoryPool::lock(PoolDiscardableMemory* dm) {
204     SkASSERT(dm != NULL);
205     if (NULL == dm->fPointer) {
206         #if SK_LAZY_CACHE_STATS
207         SkAutoMutexAcquire autoMutexAcquire(fMutex);
208         ++fCacheMisses;
209         #endif  // SK_LAZY_CACHE_STATS
210         return false;
211     }
212     SkAutoMutexAcquire autoMutexAcquire(fMutex);
213     if (NULL == dm->fPointer) {
214         // May have been purged while waiting for lock.
215         #if SK_LAZY_CACHE_STATS
216         ++fCacheMisses;
217         #endif  // SK_LAZY_CACHE_STATS
218         return false;
219     }
220     dm->fLocked = true;
221     fList.remove(dm);
222     fList.addToHead(dm);
223     #if SK_LAZY_CACHE_STATS
224     ++fCacheHits;
225     #endif  // SK_LAZY_CACHE_STATS
226     return true;
227 }
228
229 void DiscardableMemoryPool::unlock(PoolDiscardableMemory* dm) {
230     SkASSERT(dm != NULL);
231     SkAutoMutexAcquire autoMutexAcquire(fMutex);
232     dm->fLocked = false;
233     this->dumpDownTo(fBudget);
234 }
235
236 size_t DiscardableMemoryPool::getRAMUsed() {
237     return fUsed;
238 }
239 void DiscardableMemoryPool::setRAMBudget(size_t budget) {
240     SkAutoMutexAcquire autoMutexAcquire(fMutex);
241     fBudget = budget;
242     this->dumpDownTo(fBudget);
243 }
244 void DiscardableMemoryPool::dumpPool() {
245     SkAutoMutexAcquire autoMutexAcquire(fMutex);
246     this->dumpDownTo(0);
247 }
248
249 ////////////////////////////////////////////////////////////////////////////////
250 SK_DECLARE_STATIC_MUTEX(gMutex);
251 SkDiscardableMemoryPool* gPool = NULL;
252 void create_global_pool(int) {
253     SkASSERT(NULL == gPool);
254     gPool = SkDiscardableMemoryPool::Create(
255             SK_DEFAULT_GLOBAL_DISCARDABLE_MEMORY_POOL_SIZE, &gMutex);
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
267 SkDiscardableMemoryPool* SkGetGlobalDiscardableMemoryPool() {
268     SK_DECLARE_STATIC_ONCE(create_pool_once);
269     SkOnce(&create_pool_once, create_global_pool, 0, &cleanup_global_pool);
270     SkASSERT(NULL != gPool);
271     return gPool;
272 }
273
274 ////////////////////////////////////////////////////////////////////////////////