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 "SkDiscardableMemory.h"
10 #include "SkMalloc.h"
11
12 //#define TRACK_CACHEDDATA_LIFETIME
13
14 #ifdef TRACK_CACHEDDATA_LIFETIME
15 static int32_t gCachedDataCounter;
16
inc()17 static void inc() {
18 int32_t oldCount = sk_atomic_inc(&gCachedDataCounter);
19 SkDebugf("SkCachedData inc %d\n", oldCount + 1);
20 }
21
dec()22 static void dec() {
23 int32_t oldCount = sk_atomic_dec(&gCachedDataCounter);
24 SkDebugf("SkCachedData dec %d\n", oldCount - 1);
25 }
26 #else
inc()27 static void inc() {}
dec()28 static void dec() {}
29 #endif
30
SkCachedData(void * data,size_t size)31 SkCachedData::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
SkCachedData(size_t size,SkDiscardableMemory * dm)43 SkCachedData::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
~SkCachedData()55 SkCachedData::~SkCachedData() {
56 switch (fStorageType) {
57 case kMalloc_StorageType:
58 sk_free(fStorage.fMalloc);
59 break;
60 case kDiscardableMemory_StorageType:
61 delete fStorage.fDM;
62 break;
63 }
64 dec();
65 }
66
67 class SkCachedData::AutoMutexWritable {
68 public:
AutoMutexWritable(const SkCachedData * cd)69 AutoMutexWritable(const SkCachedData* cd) : fCD(const_cast<SkCachedData*>(cd)) {
70 fCD->fMutex.acquire();
71 fCD->validate();
72 }
~AutoMutexWritable()73 ~AutoMutexWritable() {
74 fCD->validate();
75 fCD->fMutex.release();
76 }
77
get()78 SkCachedData* get() { return fCD; }
operator ->()79 SkCachedData* operator->() { return fCD; }
80
81 private:
82 SkCachedData* fCD;
83 };
84
internalRef(bool fromCache) const85 void SkCachedData::internalRef(bool fromCache) const {
86 AutoMutexWritable(this)->inMutexRef(fromCache);
87 }
88
internalUnref(bool fromCache) const89 void 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 delete this;
93 }
94 }
95
96 ///////////////////////////////////////////////////////////////////////////////////////////////////
97
inMutexRef(bool fromCache)98 void 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
inMutexUnref(bool fromCache)110 bool 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
inMutexLock()139 void 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(nullptr); // signal failure to lock, contents are gone
156 }
157 break;
158 }
159 }
160
inMutexUnlock()161 void 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(nullptr); // signal that we're in an unlocked state
178 }
179
180 ///////////////////////////////////////////////////////////////////////////////////////////////////
181
182 #ifdef SK_DEBUG
validate() const183 void 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(nullptr == fData);
197 }
198 }
199 #endif
200