Home | History | Annotate | Download | only in core
      1 /*
      2  * Copyright 2014 Google Inc.
      3  *
      4  * Use of this source code is governed by a BSD-style license that can be
      5  * found in the LICENSE file.
      6  */
      7 
      8 #include "SkCachedData.h"
      9 #include "SkDiscardableMemory.h"
     10 #include "SkMalloc.h"
     11 
     12 //#define TRACK_CACHEDDATA_LIFETIME
     13 
     14 #ifdef TRACK_CACHEDDATA_LIFETIME
     15 static int32_t gCachedDataCounter;
     16 
     17 static void inc() {
     18     int32_t oldCount = sk_atomic_inc(&gCachedDataCounter);
     19     SkDebugf("SkCachedData inc %d\n", oldCount + 1);
     20 }
     21 
     22 static void dec() {
     23     int32_t oldCount = sk_atomic_dec(&gCachedDataCounter);
     24     SkDebugf("SkCachedData dec %d\n", oldCount - 1);
     25 }
     26 #else
     27 static void inc() {}
     28 static void dec() {}
     29 #endif
     30 
     31 SkCachedData::SkCachedData(void* data, size_t size)
     32     : fData(data)
     33     , fSize(size)
     34     , fRefCnt(1)
     35     , fStorageType(kMalloc_StorageType)
     36     , fInCache(false)
     37     , fIsLocked(true)
     38 {
     39     fStorage.fMalloc = data;
     40     inc();
     41 }
     42 
     43 SkCachedData::SkCachedData(size_t size, SkDiscardableMemory* dm)
     44     : fData(dm->data())
     45     , fSize(size)
     46     , fRefCnt(1)
     47     , fStorageType(kDiscardableMemory_StorageType)
     48     , fInCache(false)
     49     , fIsLocked(true)
     50 {
     51     fStorage.fDM = dm;
     52     inc();
     53 }
     54 
     55 SkCachedData::~SkCachedData() {
     56     switch (fStorageType) {
     57         case kMalloc_StorageType:
     58             sk_free(fStorage.fMalloc);
     59             break;
     60         case kDiscardableMemory_StorageType:
     61             delete fStorage.fDM;
     62             break;
     63     }
     64     dec();
     65 }
     66 
     67 class SkCachedData::AutoMutexWritable {
     68 public:
     69     AutoMutexWritable(const SkCachedData* cd) : fCD(const_cast<SkCachedData*>(cd)) {
     70         fCD->fMutex.acquire();
     71         fCD->validate();
     72     }
     73     ~AutoMutexWritable() {
     74         fCD->validate();
     75         fCD->fMutex.release();
     76     }
     77 
     78     SkCachedData* get() { return fCD; }
     79     SkCachedData* operator->() { return fCD; }
     80 
     81 private:
     82     SkCachedData* fCD;
     83 };
     84 
     85 void SkCachedData::internalRef(bool fromCache) const {
     86     AutoMutexWritable(this)->inMutexRef(fromCache);
     87 }
     88 
     89 void SkCachedData::internalUnref(bool fromCache) const {
     90     if (AutoMutexWritable(this)->inMutexUnref(fromCache)) {
     91         // can't delete inside doInternalUnref, since it is locking a mutex (which we own)
     92         delete this;
     93     }
     94 }
     95 
     96 ///////////////////////////////////////////////////////////////////////////////////////////////////
     97 
     98 void SkCachedData::inMutexRef(bool fromCache) {
     99     if ((1 == fRefCnt) && fInCache) {
    100         this->inMutexLock();
    101     }
    102 
    103     fRefCnt += 1;
    104     if (fromCache) {
    105         SkASSERT(!fInCache);
    106         fInCache = true;
    107     }
    108 }
    109 
    110 bool SkCachedData::inMutexUnref(bool fromCache) {
    111     switch (--fRefCnt) {
    112         case 0:
    113             // we're going to be deleted, so we need to be unlocked (for DiscardableMemory)
    114             if (fIsLocked) {
    115                 this->inMutexUnlock();
    116             }
    117             break;
    118         case 1:
    119             if (fInCache && !fromCache) {
    120                 // If we're down to 1 owner, and that owner is the cache, this it is safe
    121                 // to unlock (and mutate fData) even if the cache is in a different thread,
    122                 // as the cache is NOT allowed to inspect or use fData.
    123                 this->inMutexUnlock();
    124             }
    125             break;
    126         default:
    127             break;
    128     }
    129 
    130     if (fromCache) {
    131         SkASSERT(fInCache);
    132         fInCache = false;
    133     }
    134 
    135     // return true when we need to be deleted
    136     return 0 == fRefCnt;
    137 }
    138 
    139 void SkCachedData::inMutexLock() {
    140     fMutex.assertHeld();
    141 
    142     SkASSERT(!fIsLocked);
    143     fIsLocked = true;
    144 
    145     switch (fStorageType) {
    146         case kMalloc_StorageType:
    147             this->setData(fStorage.fMalloc);
    148             break;
    149         case kDiscardableMemory_StorageType:
    150             if (fStorage.fDM->lock()) {
    151                 void* ptr = fStorage.fDM->data();
    152                 SkASSERT(ptr);
    153                 this->setData(ptr);
    154             } else {
    155                 this->setData(nullptr);   // signal failure to lock, contents are gone
    156             }
    157             break;
    158     }
    159 }
    160 
    161 void SkCachedData::inMutexUnlock() {
    162     fMutex.assertHeld();
    163 
    164     SkASSERT(fIsLocked);
    165     fIsLocked = false;
    166 
    167     switch (fStorageType) {
    168         case kMalloc_StorageType:
    169             // nothing to do/check
    170             break;
    171         case kDiscardableMemory_StorageType:
    172             if (fData) {    // did the previous lock succeed?
    173                 fStorage.fDM->unlock();
    174             }
    175             break;
    176     }
    177     this->setData(nullptr);   // signal that we're in an unlocked state
    178 }
    179 
    180 ///////////////////////////////////////////////////////////////////////////////////////////////////
    181 
    182 #ifdef SK_DEBUG
    183 void SkCachedData::validate() const {
    184     if (fIsLocked) {
    185         SkASSERT((fInCache && fRefCnt > 1) || !fInCache);
    186         switch (fStorageType) {
    187             case kMalloc_StorageType:
    188                 SkASSERT(fData == fStorage.fMalloc);
    189                 break;
    190             case kDiscardableMemory_StorageType:
    191                 // fData can be null or the actual value, depending if DM's lock succeeded
    192                 break;
    193         }
    194     } else {
    195         SkASSERT((fInCache && 1 == fRefCnt) || (0 == fRefCnt));
    196         SkASSERT(nullptr == fData);
    197     }
    198 }
    199 #endif
    200