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
14class SkDiscardableMemory;
15
16class SkCachedData : ::SkNoncopyable {
17public:
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
38protected:
39    // called when fData changes. could be nullptr.
40    virtual void onDataChange(void* oldData, void* newData) {}
41
42private:
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
80public:
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