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