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