Chromium Code Reviews| Index: src/core/SkCachedData.cpp |
| diff --git a/src/core/SkCachedData.cpp b/src/core/SkCachedData.cpp |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..213294b80e992d7075fbcb99d290bdf2c9dd3a3b |
| --- /dev/null |
| +++ b/src/core/SkCachedData.cpp |
| @@ -0,0 +1,170 @@ |
| +/* |
| + * 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(1) |
| + , fStorageType(kMalloc_StorageType) |
| + , fInCache(false) |
| + , fIsLocked(true) |
| +{ |
| + fStorage.fMalloc = data; |
| +} |
| + |
| +SkCachedData::SkCachedData(size_t size, SkDiscardableMemory* dm) |
| + : fData(dm->data()) |
| + , fSize(size) |
| + , fRefCnt(1) |
| + , fStorageType(kDiscardableMemory_StorageType) |
| + , fInCache(false) |
| + , fIsLocked(true) |
| +{ |
| + fStorage.fDM = dm; |
| +} |
| + |
| +SkCachedData::~SkCachedData() { |
| + switch (fStorageType) { |
| + case kMalloc_StorageType: |
| + sk_free(fStorage.fMalloc); |
| + break; |
| + case kDiscardableMemory_StorageType: |
| + SkDELETE(fStorage.fDM); |
| + break; |
| + } |
| +} |
| + |
| +class SkCachedData::AutoMutexWritable { |
| +public: |
| + AutoMutexWritable(const SkCachedData* cd) : fCD(const_cast<SkCachedData*>(cd)) { |
| + fCD->get_mutex().acquire(); |
| + fCD->validate(); |
| + } |
| + ~AutoMutexWritable() { |
| + fCD->validate(); |
| + fCD->get_mutex().release(); |
| + } |
| + |
| + SkCachedData* get() { return fCD; } |
| + SkCachedData* operator->() { return fCD; } |
| + |
| +private: |
| + SkCachedData* fCD; |
| +}; |
| + |
| +void SkCachedData::internalRef(bool fromCache) const { |
| + AutoMutexWritable writable(this); |
|
mtklein
2014/09/27 14:47:49
I really like this pattern.
|
| + |
| + SkASSERT(!fromCache || !writable->fInCache); // we can only be in a cache once |
| + |
| + if ((1 == writable->fRefCnt) && fInCache) { |
| + writable->in_mutex_lock(); |
| + } |
| + |
| + writable->fRefCnt += 1; |
| + writable->fInCache |= fromCache; |
|
mtklein
2014/09/27 14:47:49
Just a nit, but I'd go with ||= here. It really i
reed1
2014/09/27 17:25:58
||= isn't legal afaik
However, I did rewrite to u
|
| +} |
| + |
| +bool SkCachedData::doInternalUnref(bool fromCache) const { |
| + AutoMutexWritable writable(this); |
| + |
| + switch (writable->fRefCnt) { |
|
mtklein
2014/09/27 14:47:49
These guys might read better if you decrement firs
reed1
2014/09/27 17:25:58
Done.
|
| + case 1: |
| + // we're going to be deleted, so we need to be unlocked (for DiscardableMemory) |
| + if (writable->fIsLocked) { |
| + writable->in_mutex_unlock(); |
| + } |
| + break; |
| + case 2: |
| + 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. |
| + writable->in_mutex_unlock(); |
| + } |
| + break; |
| + default: |
| + break; |
| + } |
| + |
| + if (fromCache) { |
| + SkASSERT(writable->fInCache); |
| + writable->fInCache = false; |
| + } |
| + |
| + // return true when we need to be deleted |
| + return 0 == --writable->fRefCnt; |
| +} |
| + |
| +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(); |
| + |
| + SkASSERT(!fIsLocked); |
| + fIsLocked = true; |
| + |
| + 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(); |
| + |
| + 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 |