Index: src/core/SkCachedData.cpp |
diff --git a/src/core/SkCachedData.cpp b/src/core/SkCachedData.cpp |
new file mode 100644 |
index 0000000000000000000000000000000000000000..f1fb026ba841db4f960f5f4ab5ccfc085206f926 |
--- /dev/null |
+++ b/src/core/SkCachedData.cpp |
@@ -0,0 +1,199 @@ |
+/* |
+ * Copyright 2014 Google Inc. |
+ * |
+ * Use of this source code is governed by a BSD-style license that can be |
+ * found in the LICENSE file. |
+ */ |
+ |
+#include "SkCachedData.h" |
+#include "SkRefCnt.h" |
+#include "SkDiscardableMemory.h" |
+ |
+//#define TRACK_CACHEDDATA_LIFETIME |
+ |
+#ifdef TRACK_CACHEDDATA_LIFETIME |
+static int32_t gCachedDataCounter; |
+ |
+static void inc() { |
+ int32_t oldCount = sk_atomic_inc(&gCachedDataCounter); |
+ SkDebugf("SkCachedData inc %d\n", oldCount + 1); |
+} |
+ |
+static void dec() { |
+ int32_t oldCount = sk_atomic_dec(&gCachedDataCounter); |
+ SkDebugf("SkCachedData dec %d\n", oldCount - 1); |
+} |
+#else |
+static void inc() {} |
+static void dec() {} |
+#endif |
+ |
+SkCachedData::SkCachedData(void* data, size_t size) |
+ : fData(data) |
+ , fSize(size) |
+ , fRefCnt(1) |
+ , fStorageType(kMalloc_StorageType) |
+ , fInCache(false) |
+ , fIsLocked(true) |
+{ |
+ fStorage.fMalloc = data; |
+ inc(); |
+} |
+ |
+SkCachedData::SkCachedData(size_t size, SkDiscardableMemory* dm) |
+ : fData(dm->data()) |
+ , fSize(size) |
+ , fRefCnt(1) |
+ , fStorageType(kDiscardableMemory_StorageType) |
+ , fInCache(false) |
+ , fIsLocked(true) |
+{ |
+ fStorage.fDM = dm; |
+ inc(); |
+} |
+ |
+SkCachedData::~SkCachedData() { |
+ switch (fStorageType) { |
+ case kMalloc_StorageType: |
+ sk_free(fStorage.fMalloc); |
+ break; |
+ case kDiscardableMemory_StorageType: |
+ SkDELETE(fStorage.fDM); |
+ break; |
+ } |
+ dec(); |
+} |
+ |
+class SkCachedData::AutoMutexWritable { |
+public: |
+ AutoMutexWritable(const SkCachedData* cd) : fCD(const_cast<SkCachedData*>(cd)) { |
+ fCD->fMutex.acquire(); |
+ fCD->validate(); |
+ } |
+ ~AutoMutexWritable() { |
+ fCD->validate(); |
+ fCD->fMutex.release(); |
+ } |
+ |
+ SkCachedData* get() { return fCD; } |
+ SkCachedData* operator->() { return fCD; } |
+ |
+private: |
+ SkCachedData* fCD; |
+}; |
+ |
+void SkCachedData::internalRef(bool fromCache) const { |
+ AutoMutexWritable(this)->inMutexRef(fromCache); |
+} |
+ |
+void SkCachedData::internalUnref(bool fromCache) const { |
+ if (AutoMutexWritable(this)->inMutexUnref(fromCache)) { |
+ // can't delete inside doInternalUnref, since it is locking a mutex (which we own) |
+ SkDELETE(this); |
+ } |
+} |
+ |
+/////////////////////////////////////////////////////////////////////////////////////////////////// |
+ |
+void SkCachedData::inMutexRef(bool fromCache) { |
+ if ((1 == fRefCnt) && fInCache) { |
+ this->inMutexLock(); |
+ } |
+ |
+ fRefCnt += 1; |
+ if (fromCache) { |
+ SkASSERT(!fInCache); |
+ fInCache = true; |
+ } |
+} |
+ |
+bool SkCachedData::inMutexUnref(bool fromCache) { |
+ switch (--fRefCnt) { |
+ case 0: |
+ // we're going to be deleted, so we need to be unlocked (for DiscardableMemory) |
+ if (fIsLocked) { |
+ this->inMutexUnlock(); |
+ } |
+ break; |
+ case 1: |
+ if (fInCache && !fromCache) { |
+ // If we're down to 1 owner, and that owner is the cache, this it is safe |
+ // to unlock (and mutate fData) even if the cache is in a different thread, |
+ // as the cache is NOT allowed to inspect or use fData. |
+ this->inMutexUnlock(); |
+ } |
+ break; |
+ default: |
+ break; |
+ } |
+ |
+ if (fromCache) { |
+ SkASSERT(fInCache); |
+ fInCache = false; |
+ } |
+ |
+ // return true when we need to be deleted |
+ return 0 == fRefCnt; |
+} |
+ |
+void SkCachedData::inMutexLock() { |
+ fMutex.assertHeld(); |
+ |
+ SkASSERT(!fIsLocked); |
+ fIsLocked = true; |
+ |
+ switch (fStorageType) { |
+ case kMalloc_StorageType: |
+ this->setData(fStorage.fMalloc); |
+ break; |
+ case kDiscardableMemory_StorageType: |
+ if (fStorage.fDM->lock()) { |
+ void* ptr = fStorage.fDM->data(); |
+ SkASSERT(ptr); |
+ this->setData(ptr); |
+ } else { |
+ this->setData(NULL); // signal failure to lock, contents are gone |
+ } |
+ break; |
+ } |
+} |
+ |
+void SkCachedData::inMutexUnlock() { |
+ fMutex.assertHeld(); |
+ |
+ SkASSERT(fIsLocked); |
+ fIsLocked = false; |
+ |
+ switch (fStorageType) { |
+ case kMalloc_StorageType: |
+ // nothing to do/check |
+ break; |
+ case kDiscardableMemory_StorageType: |
+ if (fData) { // did the previous lock succeed? |
+ fStorage.fDM->unlock(); |
+ } |
+ break; |
+ } |
+ this->setData(NULL); // signal that we're in an unlocked state |
+} |
+ |
+/////////////////////////////////////////////////////////////////////////////////////////////////// |
+ |
+#ifdef SK_DEBUG |
+void SkCachedData::validate() const { |
+ if (fIsLocked) { |
+ SkASSERT((fInCache && fRefCnt > 1) || !fInCache); |
+ switch (fStorageType) { |
+ case kMalloc_StorageType: |
+ SkASSERT(fData == fStorage.fMalloc); |
+ break; |
+ case kDiscardableMemory_StorageType: |
+ // fData can be null or the actual value, depending if DM's lock succeeded |
+ break; |
+ } |
+ } else { |
+ SkASSERT((fInCache && 1 == fRefCnt) || (0 == fRefCnt)); |
+ SkASSERT(NULL == fData); |
+ } |
+} |
+#endif |