1/*
2 * Copyright 2014 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 "SkCachedData.h"
9#include "SkRefCnt.h"
10#include "SkDiscardableMemory.h"
11
12//#define TRACK_CACHEDDATA_LIFETIME
13
14#ifdef TRACK_CACHEDDATA_LIFETIME
15static int32_t gCachedDataCounter;
16
17static void inc() {
18    int32_t oldCount = sk_atomic_inc(&gCachedDataCounter);
19    SkDebugf("SkCachedData inc %d\n", oldCount + 1);
20}
21
22static void dec() {
23    int32_t oldCount = sk_atomic_dec(&gCachedDataCounter);
24    SkDebugf("SkCachedData dec %d\n", oldCount - 1);
25}
26#else
27static void inc() {}
28static void dec() {}
29#endif
30
31SkCachedData::SkCachedData(void* data, size_t size)
32    : fData(data)
33    , fSize(size)
34    , fRefCnt(1)
35    , fStorageType(kMalloc_StorageType)
36    , fInCache(false)
37    , fIsLocked(true)
38{
39    fStorage.fMalloc = data;
40    inc();
41}
42
43SkCachedData::SkCachedData(size_t size, SkDiscardableMemory* dm)
44    : fData(dm->data())
45    , fSize(size)
46    , fRefCnt(1)
47    , fStorageType(kDiscardableMemory_StorageType)
48    , fInCache(false)
49    , fIsLocked(true)
50{
51    fStorage.fDM = dm;
52    inc();
53}
54
55SkCachedData::~SkCachedData() {
56    switch (fStorageType) {
57        case kMalloc_StorageType:
58            sk_free(fStorage.fMalloc);
59            break;
60        case kDiscardableMemory_StorageType:
61            SkDELETE(fStorage.fDM);
62            break;
63    }
64    dec();
65}
66
67class SkCachedData::AutoMutexWritable {
68public:
69    AutoMutexWritable(const SkCachedData* cd) : fCD(const_cast<SkCachedData*>(cd)) {
70        fCD->fMutex.acquire();
71        fCD->validate();
72    }
73    ~AutoMutexWritable() {
74        fCD->validate();
75        fCD->fMutex.release();
76    }
77
78    SkCachedData* get() { return fCD; }
79    SkCachedData* operator->() { return fCD; }
80
81private:
82    SkCachedData* fCD;
83};
84
85void SkCachedData::internalRef(bool fromCache) const {
86    AutoMutexWritable(this)->inMutexRef(fromCache);
87}
88
89void SkCachedData::internalUnref(bool fromCache) const {
90    if (AutoMutexWritable(this)->inMutexUnref(fromCache)) {
91        // can't delete inside doInternalUnref, since it is locking a mutex (which we own)
92        SkDELETE(this);
93    }
94}
95
96///////////////////////////////////////////////////////////////////////////////////////////////////
97
98void SkCachedData::inMutexRef(bool fromCache) {
99    if ((1 == fRefCnt) && fInCache) {
100        this->inMutexLock();
101    }
102
103    fRefCnt += 1;
104    if (fromCache) {
105        SkASSERT(!fInCache);
106        fInCache = true;
107    }
108}
109
110bool SkCachedData::inMutexUnref(bool fromCache) {
111    switch (--fRefCnt) {
112        case 0:
113            // we're going to be deleted, so we need to be unlocked (for DiscardableMemory)
114            if (fIsLocked) {
115                this->inMutexUnlock();
116            }
117            break;
118        case 1:
119            if (fInCache && !fromCache) {
120                // If we're down to 1 owner, and that owner is the cache, this it is safe
121                // to unlock (and mutate fData) even if the cache is in a different thread,
122                // as the cache is NOT allowed to inspect or use fData.
123                this->inMutexUnlock();
124            }
125            break;
126        default:
127            break;
128    }
129
130    if (fromCache) {
131        SkASSERT(fInCache);
132        fInCache = false;
133    }
134
135    // return true when we need to be deleted
136    return 0 == fRefCnt;
137}
138
139void SkCachedData::inMutexLock() {
140    fMutex.assertHeld();
141
142    SkASSERT(!fIsLocked);
143    fIsLocked = true;
144
145    switch (fStorageType) {
146        case kMalloc_StorageType:
147            this->setData(fStorage.fMalloc);
148            break;
149        case kDiscardableMemory_StorageType:
150            if (fStorage.fDM->lock()) {
151                void* ptr = fStorage.fDM->data();
152                SkASSERT(ptr);
153                this->setData(ptr);
154            } else {
155                this->setData(NULL);   // signal failure to lock, contents are gone
156            }
157            break;
158    }
159}
160
161void SkCachedData::inMutexUnlock() {
162    fMutex.assertHeld();
163
164    SkASSERT(fIsLocked);
165    fIsLocked = false;
166
167    switch (fStorageType) {
168        case kMalloc_StorageType:
169            // nothing to do/check
170            break;
171        case kDiscardableMemory_StorageType:
172            if (fData) {    // did the previous lock succeed?
173                fStorage.fDM->unlock();
174            }
175            break;
176    }
177    this->setData(NULL);   // signal that we're in an unlocked state
178}
179
180///////////////////////////////////////////////////////////////////////////////////////////////////
181
182#ifdef SK_DEBUG
183void SkCachedData::validate() const {
184    if (fIsLocked) {
185        SkASSERT((fInCache && fRefCnt > 1) || !fInCache);
186        switch (fStorageType) {
187            case kMalloc_StorageType:
188                SkASSERT(fData == fStorage.fMalloc);
189                break;
190            case kDiscardableMemory_StorageType:
191                // fData can be null or the actual value, depending if DM's lock succeeded
192                break;
193        }
194    } else {
195        SkASSERT((fInCache && 1 == fRefCnt) || (0 == fRefCnt));
196        SkASSERT(NULL == fData);
197    }
198}
199#endif
200