| /* | |
| * Copyright 2014 Google Inc. | |
| * | |
| * Use of this source code is governed by a BSD-style license that can be | |
| * found in the LICENSE file. | |
| */ | |
| #ifndef SkCachedData_DEFINED | |
| #define SkCachedData_DEFINED | |
| #include "SkMutex.h" | |
| #include "SkNoncopyable.h" | |
| #include "SkTypes.h" | |
| class SkDiscardableMemory; | |
| class SkCachedData : ::SkNoncopyable { | |
| public: | |
| SkCachedData(void* mallocData, size_t size); | |
| SkCachedData(size_t size, SkDiscardableMemory*); | |
| virtual ~SkCachedData(); | |
| size_t size() const { return fSize; } | |
| const void* data() const { return fData; } | |
| void* writable_data() { return fData; } | |
| void ref() const { this->internalRef(false); } | |
| void unref() const { this->internalUnref(false); } | |
| int testing_only_getRefCnt() const { return fRefCnt; } | |
| bool testing_only_isLocked() const { return fIsLocked; } | |
| bool testing_only_isInCache() const { return fInCache; } | |
| SkDiscardableMemory* diagnostic_only_getDiscardable() const { | |
| return kDiscardableMemory_StorageType == fStorageType ? fStorage.fDM : nullptr; | |
| } | |
| protected: | |
| // called when fData changes. could be nullptr. | |
| virtual void onDataChange(void* oldData, void* newData) {} | |
| private: | |
| SkMutex fMutex; // could use a pool of these... | |
| enum StorageType { | |
| kDiscardableMemory_StorageType, | |
| kMalloc_StorageType | |
| }; | |
| union { | |
| SkDiscardableMemory* fDM; | |
| void* fMalloc; | |
| } fStorage; | |
| void* fData; | |
| size_t fSize; | |
| int fRefCnt; // low-bit means we're owned by the cache | |
| StorageType fStorageType; | |
| bool fInCache; | |
| bool fIsLocked; | |
| void internalRef(bool fromCache) const; | |
| void internalUnref(bool fromCache) const; | |
| void inMutexRef(bool fromCache); | |
| bool inMutexUnref(bool fromCache); // returns true if we should delete "this" | |
| void inMutexLock(); | |
| void inMutexUnlock(); | |
| // called whenever our fData might change (lock or unlock) | |
| void setData(void* newData) { | |
| if (newData != fData) { | |
| // notify our subclasses of the change | |
| this->onDataChange(fData, newData); | |
| fData = newData; | |
| } | |
| } | |
| class AutoMutexWritable; | |
| public: | |
| #ifdef SK_DEBUG | |
| void validate() const; | |
| #else | |
| void validate() const {} | |
| #endif | |
| /* | |
| * Attaching a data to to a SkResourceCache (only one at a time) enables the data to be | |
| * unlocked when the cache is the only owner, thus freeing it to be purged (assuming the | |
| * data is backed by a SkDiscardableMemory). | |
| * | |
| * When attached, it also automatically attempts to "lock" the data when the first client | |
| * ref's the data (typically from a find(key, visitor) call). | |
| * | |
| * Thus the data will always be "locked" when a non-cache has a ref on it (whether or not | |
| * the lock succeeded to recover the memory -- check data() to see if it is nullptr). | |
| */ | |
| /* | |
| * Call when adding this instance to a SkResourceCache::Rec subclass | |
| * (typically in the Rec's constructor). | |
| */ | |
| void attachToCacheAndRef() const { this->internalRef(true); } | |
| /* | |
| * Call when removing this instance from a SkResourceCache::Rec subclass | |
| * (typically in the Rec's destructor). | |
| */ | |
| void detachFromCacheAndUnref() const { this->internalUnref(true); } | |
| }; | |
| #endif |