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