/* * 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" 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: delete fStorage.fDM; break; } } class SkCachedData::AutoMutexWritable { public: AutoMutexWritable(const SkCachedData* cd) : fCD(const_cast(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