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