• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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