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 "SkRefCnt.h"
10 #include "SkDiscardableMemory.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             SkDELETE(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         SkDELETE(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(NULL);   // 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(NULL);   // 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(NULL == fData);
197     }
198 }
199 #endif
200