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