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 #ifndef SkCachedData_DEFINED
9 #define SkCachedData_DEFINED
10 
11 #include "SkThread.h"
12 
13 class SkDiscardableMemory;
14 
15 class SkCachedData : ::SkNoncopyable {
16 public:
17     SkCachedData(void* mallocData, size_t size);
18     SkCachedData(size_t size, SkDiscardableMemory*);
19     virtual ~SkCachedData();
20 
size()21     size_t size() const { return fSize; }
data()22     const void* data() const { return fData; }
23 
writable_data()24     void* writable_data() { return fData; }
25 
ref()26     void ref() const { this->internalRef(false); }
unref()27     void unref() const { this->internalUnref(false); }
28 
testing_only_getRefCnt()29     int testing_only_getRefCnt() const { return fRefCnt; }
testing_only_isLocked()30     bool testing_only_isLocked() const { return fIsLocked; }
testing_only_isInCache()31     bool testing_only_isInCache() const { return fInCache; }
32 
33 protected:
34     // called when fData changes. could be NULL.
onDataChange(void * oldData,void * newData)35     virtual void onDataChange(void* oldData, void* newData) {}
36 
37 private:
38     SkMutex fMutex;     // could use a pool of these...
39 
40     enum StorageType {
41         kDiscardableMemory_StorageType,
42         kMalloc_StorageType
43     };
44 
45     union {
46         SkDiscardableMemory*    fDM;
47         void*                   fMalloc;
48     } fStorage;
49     void*       fData;
50     size_t      fSize;
51     int         fRefCnt;    // low-bit means we're owned by the cache
52     StorageType fStorageType;
53     bool        fInCache;
54     bool        fIsLocked;
55 
56     void internalRef(bool fromCache) const;
57     void internalUnref(bool fromCache) const;
58 
59     void inMutexRef(bool fromCache);
60     bool inMutexUnref(bool fromCache);  // returns true if we should delete "this"
61     void inMutexLock();
62     void inMutexUnlock();
63 
64     // called whenever our fData might change (lock or unlock)
setData(void * newData)65     void setData(void* newData) {
66         if (newData != fData) {
67             // notify our subclasses of the change
68             this->onDataChange(fData, newData);
69             fData = newData;
70         }
71     }
72 
73     class AutoMutexWritable;
74 
75 public:
76 #ifdef SK_DEBUG
77     void validate() const;
78 #else
79     void validate() const {}
80 #endif
81 
82    /*
83      *  Attaching a data to to a SkResourceCache (only one at a time) enables the data to be
84      *  unlocked when the cache is the only owner, thus freeing it to be purged (assuming the
85      *  data is backed by a SkDiscardableMemory).
86      *
87      *  When attached, it also automatically attempts to "lock" the data when the first client
88      *  ref's the data (typically from a find(key, visitor) call).
89      *
90      *  Thus the data will always be "locked" when a non-cache has a ref on it (whether or not
91      *  the lock succeeded to recover the memory -- check data() to see if it is NULL).
92      */
93 
94     /*
95      *  Call when adding this instance to a SkResourceCache::Rec subclass
96      *  (typically in the Rec's constructor).
97      */
attachToCacheAndRef()98     void attachToCacheAndRef() const { this->internalRef(true); }
99 
100     /*
101      *  Call when removing this instance from a SkResourceCache::Rec subclass
102      *  (typically in the Rec's destructor).
103      */
detachFromCacheAndUnref()104     void detachFromCacheAndUnref() const { this->internalUnref(true); }
105 };
106 
107 #endif
108