* 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 "include/private/SkMalloc.h"
#include "src/core/SkCachedData.h"
#include "src/core/SkDiscardableMemory.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<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)) {
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:
if (fIsLocked) {
this->inMutexUnlock();
}
break;
case 1:
if (fInCache && !fromCache) {
this->inMutexUnlock();
}
break;
default:
break;
}
if (fromCache) {
SkASSERT(fInCache);
fInCache = false;
}
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);
}
break;
}
}
void SkCachedData::inMutexUnlock() {
fMutex.assertHeld();
SkASSERT(fIsLocked);
fIsLocked = false;
switch (fStorageType) {
case kMalloc_StorageType:
break;
case kDiscardableMemory_StorageType:
if (fData) {
fStorage.fDM->unlock();
}
break;
}
this->setData(nullptr);
}
#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:
break;
}
} else {
SkASSERT((fInCache && 1 == fRefCnt) || (0 == fRefCnt));
SkASSERT(nullptr == fData);
}
}
#endif