1// Copyright (C) 2016 and later: Unicode, Inc. and others.
2// License & terms of use: http://www.unicode.org/copyright.html
3/*
4******************************************************************************
5* Copyright (C) 2015-2016, International Business Machines
6* Corporation and others.  All Rights Reserved.
7******************************************************************************
8* sharedobject.h
9*/
10
11#ifndef __SHAREDOBJECT_H__
12#define __SHAREDOBJECT_H__
13
14
15#include "unicode/uobject.h"
16#include "umutex.h"
17
18U_NAMESPACE_BEGIN
19
20/**
21 * Base class for unified cache exposing enough methods to SharedObject
22 * instances to allow their addRef() and removeRef() methods to
23 * update cache metrics. No other part of ICU, except for SharedObject,
24 * should directly call the methods of this base class.
25 */
26class U_COMMON_API UnifiedCacheBase : public UObject {
27public:
28    UnifiedCacheBase() { }
29
30    /**
31     * Called by addRefWhileHoldingCacheLock() when the hard reference count
32     * of its instance goes from 0 to 1.
33     */
34    virtual void incrementItemsInUse() const = 0;
35
36    /**
37     * Called by removeRef() when the hard reference count of its instance
38     * drops from 1 to 0.
39     */
40    virtual void decrementItemsInUseWithLockingAndEviction() const = 0;
41
42    /**
43     * Called by removeRefWhileHoldingCacheLock() when the hard reference
44     * count of its instance drops from 1 to 0.
45     */
46    virtual void decrementItemsInUse() const = 0;
47    virtual ~UnifiedCacheBase();
48private:
49    UnifiedCacheBase(const UnifiedCacheBase &);
50    UnifiedCacheBase &operator=(const UnifiedCacheBase &);
51};
52
53/**
54 * Base class for shared, reference-counted, auto-deleted objects.
55 * Subclasses can be immutable.
56 * If they are mutable, then they must implement their copy constructor
57 * so that copyOnWrite() works.
58 *
59 * Either stack-allocate, use LocalPointer, or use addRef()/removeRef().
60 * Sharing requires reference-counting.
61 */
62class U_COMMON_API SharedObject : public UObject {
63public:
64    /** Initializes totalRefCount, softRefCount to 0. */
65    SharedObject() :
66            totalRefCount(0),
67            softRefCount(0),
68            hardRefCount(0),
69            cachePtr(NULL) {}
70
71    /** Initializes totalRefCount, softRefCount to 0. */
72    SharedObject(const SharedObject &other) :
73            UObject(other),
74            totalRefCount(0),
75            softRefCount(0),
76            hardRefCount(0),
77            cachePtr(NULL) {}
78
79    virtual ~SharedObject();
80
81    /**
82     * Increments the number of references to this object. Thread-safe.
83     */
84    void addRef() const { addRef(FALSE); }
85
86    /**
87     * Increments the number of references to this object.
88     * Must be called only from within the internals of UnifiedCache and
89     * only while the cache global mutex is held.
90     */
91    void addRefWhileHoldingCacheLock() const { addRef(TRUE); }
92
93    /**
94     * Increments the number of soft references to this object.
95     * Must be called only from within the internals of UnifiedCache and
96     * only while the cache global mutex is held.
97     */
98    void addSoftRef() const;
99
100    /**
101     * Decrements the number of references to this object. Thread-safe.
102     */
103    void removeRef() const { removeRef(FALSE); }
104
105    /**
106     * Decrements the number of references to this object.
107     * Must be called only from within the internals of UnifiedCache and
108     * only while the cache global mutex is held.
109     */
110    void removeRefWhileHoldingCacheLock() const { removeRef(TRUE); }
111
112    /**
113     * Decrements the number of soft references to this object.
114     * Must be called only from within the internals of UnifiedCache and
115     * only while the cache global mutex is held.
116     */
117    void removeSoftRef() const;
118
119    /**
120     * Returns the reference counter including soft references.
121     * Uses a memory barrier.
122     */
123    int32_t getRefCount() const;
124
125    /**
126     * Returns the count of soft references only.
127     * Must be called only from within the internals of UnifiedCache and
128     * only while the cache global mutex is held.
129     */
130    int32_t getSoftRefCount() const { return softRefCount; }
131
132    /**
133     * Returns the count of hard references only. Uses a memory barrier.
134     * Used for testing the cache. Regular clients won't need this.
135     */
136    int32_t getHardRefCount() const;
137
138    /**
139     * If noHardReferences() == TRUE then this object has no hard references.
140     * Must be called only from within the internals of UnifiedCache.
141     */
142    inline UBool noHardReferences() const { return getHardRefCount() == 0; }
143
144    /**
145     * If hasHardReferences() == TRUE then this object has hard references.
146     * Must be called only from within the internals of UnifiedCache.
147     */
148    inline UBool hasHardReferences() const { return getHardRefCount() != 0; }
149
150    /**
151     * If noSoftReferences() == TRUE then this object has no soft references.
152     * Must be called only from within the internals of UnifiedCache and
153     * only while the cache global mutex is held.
154     */
155    UBool noSoftReferences() const { return (softRefCount == 0); }
156
157    /**
158     * Deletes this object if it has no references or soft references.
159     */
160    void deleteIfZeroRefCount() const;
161
162    /**
163     * @internal For UnifedCache use only to register this object with itself.
164     *   Must be called before this object is exposed to multiple threads.
165     */
166    void registerWithCache(const UnifiedCacheBase *ptr) const {
167        cachePtr = ptr;
168    }
169
170    /**
171     * Returns a writable version of ptr.
172     * If there is exactly one owner, then ptr itself is returned as a
173     *  non-const pointer.
174     * If there are multiple owners, then ptr is replaced with a
175     * copy-constructed clone,
176     * and that is returned.
177     * Returns NULL if cloning failed.
178     *
179     * T must be a subclass of SharedObject.
180     */
181    template<typename T>
182    static T *copyOnWrite(const T *&ptr) {
183        const T *p = ptr;
184        if(p->getRefCount() <= 1) { return const_cast<T *>(p); }
185        T *p2 = new T(*p);
186        if(p2 == NULL) { return NULL; }
187        p->removeRef();
188        ptr = p2;
189        p2->addRef();
190        return p2;
191    }
192
193    /**
194     * Makes dest an owner of the object pointed to by src while adjusting
195     * reference counts and deleting the previous object dest pointed to
196     * if necessary. Before this call is made, dest must either be NULL or
197     * be included in the reference count of the object it points to.
198     *
199     * T must be a subclass of SharedObject.
200     */
201    template<typename T>
202    static void copyPtr(const T *src, const T *&dest) {
203        if(src != dest) {
204            if(dest != NULL) { dest->removeRef(); }
205            dest = src;
206            if(src != NULL) { src->addRef(); }
207        }
208    }
209
210    /**
211     * Equivalent to copyPtr(NULL, dest).
212     */
213    template<typename T>
214    static void clearPtr(const T *&ptr) {
215        if (ptr != NULL) {
216            ptr->removeRef();
217            ptr = NULL;
218        }
219    }
220
221private:
222    mutable u_atomic_int32_t totalRefCount;
223
224    // Any thread modifying softRefCount must hold the global cache mutex
225    mutable int32_t softRefCount;
226
227    mutable u_atomic_int32_t hardRefCount;
228    mutable const UnifiedCacheBase *cachePtr;
229    void addRef(UBool withCacheLock) const;
230    void removeRef(UBool withCacheLock) const;
231
232};
233
234U_NAMESPACE_END
235
236#endif
237