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 
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