| 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;
|
| + }
|
| +}
|
| +
|
|
|