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