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 "include/private/SkMalloc.h"
9#include "src/core/SkCachedData.h"
10#include "src/core/SkDiscardableMemory.h"
11
12SkCachedData::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
23SkCachedData::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
34SkCachedData::~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
45class SkCachedData::AutoMutexWritable {
46public:
47 AutoMutexWritable(const SkCachedData* cd) : fCD(const_cast<SkCachedData*>(cd)) {
48 fCD->fMutex.acquire();
49 fCD->validate();
50 }
51 ~AutoMutexWritable() {
52 fCD->validate();
53 fCD->fMutex.release();
54 }
55
56 SkCachedData* get() { return fCD; }
57 SkCachedData* operator->() { return fCD; }
58
59private:
60 SkCachedData* fCD;
61};
62
63void SkCachedData::internalRef(bool fromCache) const {
64 AutoMutexWritable(this)->inMutexRef(fromCache);
65}
66
67void 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
76void 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
88bool 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
117void 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
139void 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
161void 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