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