| /* |
| * 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 "SkDiscardableMemory.h" |
| #include "SkMalloc.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: |
| delete 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) |
| delete 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(nullptr); // 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(nullptr); // 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(nullptr == fData); |
| } |
| } |
| #endif |