summaryrefslogtreecommitdiffstats
path: root/intl/icu/source/common/sharedobject.h
blob: 0e53cfb7abc09f0f60e8aa7aac90c35530da702a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
// Copyright (C) 2016 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
/*
******************************************************************************
* Copyright (C) 2015-2016, International Business Machines
* Corporation and others.  All Rights Reserved.
******************************************************************************
* sharedobject.h
*/

#ifndef __SHAREDOBJECT_H__
#define __SHAREDOBJECT_H__


#include "unicode/uobject.h"
#include "umutex.h"

U_NAMESPACE_BEGIN

/**
 * Base class for unified cache exposing enough methods to SharedObject
 * instances to allow their addRef() and removeRef() methods to
 * update cache metrics. No other part of ICU, except for SharedObject,
 * should directly call the methods of this base class.
 */
class U_COMMON_API UnifiedCacheBase : public UObject {
public:
    UnifiedCacheBase() { }

    /**
     * Called by addRefWhileHoldingCacheLock() when the hard reference count
     * of its instance goes from 0 to 1.
     */
    virtual void incrementItemsInUse() const = 0;

    /**
     * Called by removeRef() when the hard reference count of its instance
     * drops from 1 to 0.
     */
    virtual void decrementItemsInUseWithLockingAndEviction() const = 0;

    /**
     * Called by removeRefWhileHoldingCacheLock() when the hard reference
     * count of its instance drops from 1 to 0.
     */
    virtual void decrementItemsInUse() const = 0;
    virtual ~UnifiedCacheBase();
private:
    UnifiedCacheBase(const UnifiedCacheBase &);
    UnifiedCacheBase &operator=(const UnifiedCacheBase &);
};

/**
 * Base class for shared, reference-counted, auto-deleted objects.
 * Subclasses can be immutable.
 * If they are mutable, then they must implement their copy constructor
 * so that copyOnWrite() works.
 *
 * Either stack-allocate, use LocalPointer, or use addRef()/removeRef().
 * Sharing requires reference-counting.
 */
class U_COMMON_API SharedObject : public UObject {
public:
    /** Initializes totalRefCount, softRefCount to 0. */
    SharedObject() :
            totalRefCount(0),
            softRefCount(0),
            hardRefCount(0),
            cachePtr(NULL) {}

    /** Initializes totalRefCount, softRefCount to 0. */
    SharedObject(const SharedObject &other) :
            UObject(other),
            totalRefCount(0),
            softRefCount(0),
            hardRefCount(0),
            cachePtr(NULL) {}

    virtual ~SharedObject();

    /**
     * Increments the number of references to this object. Thread-safe.
     */
    void addRef() const { addRef(FALSE); }

    /**
     * Increments the number of references to this object.
     * Must be called only from within the internals of UnifiedCache and
     * only while the cache global mutex is held.
     */
    void addRefWhileHoldingCacheLock() const { addRef(TRUE); }

    /**
     * Increments the number of soft references to this object.
     * Must be called only from within the internals of UnifiedCache and
     * only while the cache global mutex is held.
     */
    void addSoftRef() const;

    /**
     * Decrements the number of references to this object. Thread-safe.
     */
    void removeRef() const { removeRef(FALSE); }

    /**
     * Decrements the number of references to this object.
     * Must be called only from within the internals of UnifiedCache and
     * only while the cache global mutex is held.
     */
    void removeRefWhileHoldingCacheLock() const { removeRef(TRUE); }

    /**
     * Decrements the number of soft references to this object.
     * Must be called only from within the internals of UnifiedCache and
     * only while the cache global mutex is held.
     */
    void removeSoftRef() const;

    /**
     * Returns the reference counter including soft references.
     * Uses a memory barrier.
     */
    int32_t getRefCount() const;

    /**
     * Returns the count of soft references only.
     * Must be called only from within the internals of UnifiedCache and
     * only while the cache global mutex is held.
     */
    int32_t getSoftRefCount() const { return softRefCount; }

    /**
     * Returns the count of hard references only. Uses a memory barrier.
     * Used for testing the cache. Regular clients won't need this.
     */
    int32_t getHardRefCount() const;

    /**
     * If noHardReferences() == TRUE then this object has no hard references.
     * Must be called only from within the internals of UnifiedCache.
     */
    inline UBool noHardReferences() const { return getHardRefCount() == 0; }

    /**
     * If hasHardReferences() == TRUE then this object has hard references.
     * Must be called only from within the internals of UnifiedCache.
     */
    inline UBool hasHardReferences() const { return getHardRefCount() != 0; }

    /**
     * If noSoftReferences() == TRUE then this object has no soft references.
     * Must be called only from within the internals of UnifiedCache and
     * only while the cache global mutex is held.
     */
    UBool noSoftReferences() const { return (softRefCount == 0); }

    /**
     * Deletes this object if it has no references or soft references.
     */
    void deleteIfZeroRefCount() const;

    /**
     * @internal For UnifedCache use only to register this object with itself.
     *   Must be called before this object is exposed to multiple threads.
     */ 
    void registerWithCache(const UnifiedCacheBase *ptr) const {
        cachePtr = ptr;
    }
        
    /**
     * Returns a writable version of ptr.
     * If there is exactly one owner, then ptr itself is returned as a
     *  non-const pointer.
     * If there are multiple owners, then ptr is replaced with a 
     * copy-constructed clone,
     * and that is returned.
     * Returns NULL if cloning failed.
     *
     * T must be a subclass of SharedObject.
     */
    template<typename T>
    static T *copyOnWrite(const T *&ptr) {
        const T *p = ptr;
        if(p->getRefCount() <= 1) { return const_cast<T *>(p); }
        T *p2 = new T(*p);
        if(p2 == NULL) { return NULL; }
        p->removeRef();
        ptr = p2;
        p2->addRef();
        return p2;
    }

    /**
     * Makes dest an owner of the object pointed to by src while adjusting
     * reference counts and deleting the previous object dest pointed to
     * if necessary. Before this call is made, dest must either be NULL or
     * be included in the reference count of the object it points to. 
     *
     * T must be a subclass of SharedObject.
     */
    template<typename T>
    static void copyPtr(const T *src, const T *&dest) {
        if(src != dest) {
            if(dest != NULL) { dest->removeRef(); }
            dest = src;
            if(src != NULL) { src->addRef(); }
        }
    }

    /**
     * Equivalent to copyPtr(NULL, dest).
     */
    template<typename T>
    static void clearPtr(const T *&ptr) {
        if (ptr != NULL) {
            ptr->removeRef();
            ptr = NULL;
        }
    }

private:
    mutable u_atomic_int32_t totalRefCount;

    // Any thread modifying softRefCount must hold the global cache mutex
    mutable int32_t softRefCount;

    mutable u_atomic_int32_t hardRefCount;
    mutable const UnifiedCacheBase *cachePtr;
    void addRef(UBool withCacheLock) const;
    void removeRef(UBool withCacheLock) const;

};

U_NAMESPACE_END

#endif