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 "SkLazyPtr.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
18namespace {
19
20class PoolDiscardableMemory;
21
22/**
23 *  This non-global pool can be used for unit tests to verify that the
24 *  pool works.
25 */
26class DiscardableMemoryPool : public SkDiscardableMemoryPool {
27public:
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
53private:
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 */
77class PoolDiscardableMemory : public SkDiscardableMemory {
78public:
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;
86private:
87    SK_DECLARE_INTERNAL_LLIST_INTERFACE(PoolDiscardableMemory);
88    DiscardableMemoryPool* const fPool;
89    bool                         fLocked;
90    void*                        fPointer;
91    const size_t                 fBytes;
92};
93
94PoolDiscardableMemory::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
107PoolDiscardableMemory::~PoolDiscardableMemory() {
108    SkASSERT(!fLocked); // contract for SkDiscardableMemory
109    fPool->free(this);
110    fPool->unref();
111}
112
113bool PoolDiscardableMemory::lock() {
114    SkASSERT(!fLocked); // contract for SkDiscardableMemory
115    return fPool->lock(this);
116}
117
118void* PoolDiscardableMemory::data() {
119    SkASSERT(fLocked); // contract for SkDiscardableMemory
120    return fPointer;
121}
122
123void PoolDiscardableMemory::unlock() {
124    SkASSERT(fLocked); // contract for SkDiscardableMemory
125    fPool->unlock(this);
126}
127
128////////////////////////////////////////////////////////////////////////////////
129
130DiscardableMemoryPool::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}
140DiscardableMemoryPool::~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
147void DiscardableMemoryPool::dumpDownTo(size_t budget) {
148    if (fMutex != NULL) {
149        fMutex->assertHeld();
150    }
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
175SkDiscardableMemory* 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
189void 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
203bool 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
229void DiscardableMemoryPool::unlock(PoolDiscardableMemory* dm) {
230    SkASSERT(dm != NULL);
231    SkAutoMutexAcquire autoMutexAcquire(fMutex);
232    dm->fLocked = false;
233    this->dumpDownTo(fBudget);
234}
235
236size_t DiscardableMemoryPool::getRAMUsed() {
237    return fUsed;
238}
239void DiscardableMemoryPool::setRAMBudget(size_t budget) {
240    SkAutoMutexAcquire autoMutexAcquire(fMutex);
241    fBudget = budget;
242    this->dumpDownTo(fBudget);
243}
244void DiscardableMemoryPool::dumpPool() {
245    SkAutoMutexAcquire autoMutexAcquire(fMutex);
246    this->dumpDownTo(0);
247}
248
249////////////////////////////////////////////////////////////////////////////////
250SK_DECLARE_STATIC_MUTEX(gMutex);
251SkDiscardableMemoryPool* create_global_pool() {
252    return SkDiscardableMemoryPool::Create(SK_DEFAULT_GLOBAL_DISCARDABLE_MEMORY_POOL_SIZE,
253                                           &gMutex);
254}
255
256}  // namespace
257
258SkDiscardableMemoryPool* SkDiscardableMemoryPool::Create(size_t size, SkBaseMutex* mutex) {
259    return SkNEW_ARGS(DiscardableMemoryPool, (size, mutex));
260}
261
262SkDiscardableMemoryPool* SkGetGlobalDiscardableMemoryPool() {
263    SK_DECLARE_STATIC_LAZY_PTR(SkDiscardableMemoryPool, global, create_global_pool);
264    return global.get();
265}
266
267// defined in SkImageGenerator.h
268void SkPurgeGlobalDiscardableMemoryPool() {
269    SkGetGlobalDiscardableMemoryPool()->dumpPool();
270}
271////////////////////////////////////////////////////////////////////////////////
272