Index: src/core/SkCachedData.cpp |
diff --git a/src/core/SkCachedData.cpp b/src/core/SkCachedData.cpp |
new file mode 100644 |
index 0000000000000000000000000000000000000000..5142c523962c7c84b1fbdae272637b917e5b9c4a |
--- /dev/null |
+++ b/src/core/SkCachedData.cpp |
@@ -0,0 +1,159 @@ |
+/* |
+ * 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_priv.h" |
+#include "SkRefCnt.h" |
+#include "SkDiscardableMemory.h" |
+ |
+SkCachedData::SkCachedData(void* data, size_t size) |
+ : fData(data) |
+ , fSize(size) |
+ , fRefCnt(2) // 1 owner, not in cache |
+{ |
+ fStorage.fMalloc = data; |
+ fStorageType = kMalloc_StorageType; |
+} |
+ |
+SkCachedData::SkCachedData(size_t size, SkDiscardableMemory* dm) |
+ : fData(dm->data()) |
+ , fSize(size) |
+ , fRefCnt(2) // 1 owner, not in cache |
+{ |
+ fStorage.fDM = dm; |
+ fStorageType = kDiscardableMemory_StorageType; |
+} |
+ |
+SkCachedData::~SkCachedData() { |
+ switch (fStorageType) { |
+ case kMalloc_StorageType: |
+ sk_free(fStorage.fMalloc); |
+ break; |
+ case kDiscardableMemory_StorageType: |
+ SkDELETE(fStorage.fDM); |
+ break; |
+ } |
+} |
+ |
+void SkCachedData::internalRef(bool fromCache) const { |
+ SkCachedData* writable = const_cast<SkCachedData*>(this); |
+ SkAutoMutexAcquire ama(writable->get_mutex()); |
+ |
+ int32_t amount; |
+ if (fromCache) { |
+ // assert that we are not already attached (i.e. the low-bit is 0) |
+ SkASSERT(0 == (writable->fRefCnt & 1)); |
+ amount = 3; |
+ } else { |
+ // from client |
+ amount = 2; |
+ } |
+ |
+ const int32_t new_count = (writable->fRefCnt += amount); |
+ const int new_owners = new_count >> 1; |
+ const int in_cache = new_count & 1; |
+ |
+ SkASSERT(new_owners >= 2); |
+ |
+ if (2 == new_owners && !fromCache) { |
+ if (in_cache) { |
+ writable->in_mutex_lock(); |
+ } |
+ } |
+} |
+ |
+bool SkCachedData::doInternalUnref(bool fromCache) const { |
+ SkCachedData* writable = const_cast<SkCachedData*>(this); |
+ SkAutoMutexAcquire ama(writable->get_mutex()); |
+ |
+ int32_t amount; |
+ if (fromCache) { |
+ // assert that we are already attached (i.e. the low-bit is 1) |
+ SkASSERT(1 == (fRefCnt & 1)); |
+ amount = 3; |
+ } else { |
+ // from client |
+ amount = 2; |
+ } |
+ |
+ const int32_t new_count = (writable->fRefCnt -= amount); |
+ const int new_owners = new_count >> 1; |
+ const int in_cache = new_count & 1; |
+ |
+ switch (new_owners) { |
+ case 0: |
+ writable->in_mutex_prepareToDelete(); |
+ break; |
+ case 1: |
+ if (in_cache) { |
+ // 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. |
+ writable->in_mutex_unlock(); |
+ } |
+ break; |
+ default: |
+ break; |
+ } |
+ return 0 == new_owners; |
+} |
+ |
+void SkCachedData::internalUnref(bool fromCache) const { |
+ if (this->doInternalUnref(fromCache)) { |
+ // can't delete inside doInternalUnref, since it is locking a mutex (which we own) |
+ SkDELETE(this); |
+ } |
+} |
+ |
+void SkCachedData::in_mutex_lock() { |
+ this->assert_in_mutex(); |
+ |
+ switch (fStorageType) { |
+ case kMalloc_StorageType: |
+ this->setData(fStorage.fMalloc); |
+ break; |
+ case kDiscardableMemory_StorageType: |
+ if (fStorage.fDM->lock()) { |
+ this->setData(fStorage.fDM->data()); |
+ } else { |
+ this->setData(NULL); // signal failure to lock, contents are gone |
+ } |
+ break; |
+ } |
+} |
+ |
+void SkCachedData::in_mutex_unlock() { |
+ this->assert_in_mutex(); |
+ |
+ 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 |
+} |
+ |
+void SkCachedData::in_mutex_prepareToDelete() { |
+ this->assert_in_mutex(); |
+ |
+ switch (fStorageType) { |
+ case kMalloc_StorageType: |
+ // nothing to do/check |
+ break; |
+ case kDiscardableMemory_StorageType: |
+ // discardable request that it be unlocked before being deleted |
+ if (fData) { |
+ fStorage.fDM->unlock(); |
+ } |
+ break; |
+ } |
+} |
+ |