SkDiscardableMemoryPool.cpp revision 1d0654f69d1d0c3bb565fba018a11c77f25bc55e
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 */
19class SkPoolDiscardableMemory : public SkDiscardableMemory {
20public:
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;
28private:
29    SK_DECLARE_INTERNAL_LLIST_INTERFACE(SkPoolDiscardableMemory);
30    SkDiscardableMemoryPool* const fPool;
31    bool                           fLocked;
32    void*                          fPointer;
33    const size_t                   fBytes;
34};
35
36SkPoolDiscardableMemory::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
49SkPoolDiscardableMemory::~SkPoolDiscardableMemory() {
50    SkASSERT(!fLocked); // contract for SkDiscardableMemory
51    fPool->free(this);
52    fPool->unref();
53}
54
55bool SkPoolDiscardableMemory::lock() {
56    SkASSERT(!fLocked); // contract for SkDiscardableMemory
57    return fPool->lock(this);
58}
59
60void* SkPoolDiscardableMemory::data() {
61    SkASSERT(fLocked); // contract for SkDiscardableMemory
62    return fPointer;
63}
64
65void SkPoolDiscardableMemory::unlock() {
66    SkASSERT(fLocked); // contract for SkDiscardableMemory
67    fPool->unlock(this);
68}
69
70////////////////////////////////////////////////////////////////////////////////
71
72SkDiscardableMemoryPool::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}
82SkDiscardableMemoryPool::~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
89void 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
117SkDiscardableMemory* 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
131void 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
145bool 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
171void SkDiscardableMemoryPool::unlock(SkPoolDiscardableMemory* dm) {
172    SkASSERT(dm != NULL);
173    SkAutoMutexAcquire autoMutexAcquire(fMutex);
174    dm->fLocked = false;
175    this->dumpDownTo(fBudget);
176}
177
178size_t SkDiscardableMemoryPool::getRAMUsed() {
179    return fUsed;
180}
181void SkDiscardableMemoryPool::setRAMBudget(size_t budget) {
182    SkAutoMutexAcquire autoMutexAcquire(fMutex);
183    fBudget = budget;
184    this->dumpDownTo(fBudget);
185}
186void SkDiscardableMemoryPool::dumpPool() {
187    SkAutoMutexAcquire autoMutexAcquire(fMutex);
188    this->dumpDownTo(0);
189}
190
191////////////////////////////////////////////////////////////////////////////////
192SK_DECLARE_STATIC_MUTEX(gMutex);
193static 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}
199SkDiscardableMemoryPool* 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