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