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