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