| /* | |
| * 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 |