• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2 *******************************************************************************
3 * Copyright (C) 2014, International Business Machines Corporation and
4 * others. All Rights Reserved.
5 *******************************************************************************
6 *
7 * File SHAREDPTR.H
8 *******************************************************************************
9 */
10 
11 #ifndef __SHARED_PTR_H__
12 #define __SHARED_PTR_H__
13 
14 #include "unicode/uobject.h"
15 #include "umutex.h"
16 #include "uassert.h"
17 
18 U_NAMESPACE_BEGIN
19 
20 // Wrap u_atomic_int32_t in a UMemory so that we allocate them in the same
21 // way we allocate all other ICU objects.
22 struct AtomicInt : public UMemory {
23     u_atomic_int32_t value;
24 };
25 
26 /**
27  * SharedPtr are shared pointers that support copy-on-write sematics.
28  * SharedPtr makes the act of copying large objects cheap by deferring the
29  * cost of the copy to the first write operation after the copy.
30  *
31  * A SharedPtr<T> instance can refer to no object or an object of type T.
32  * T must have a clone() method that copies
33  * the object and returns a pointer to the copy. Copy and assignment of
34  * SharedPtr instances are cheap because they only involve copying or
35  * assigning the SharedPtr instance, not the T object which could be large.
36  * Although many SharedPtr<T> instances may refer to the same T object,
37  * clients can still assume that each SharedPtr<T> instance has its own
38  * private instance of T because each SharedPtr<T> instance offers only a
39  * const view of its T object through normal pointer operations. If a caller
40  * must change a T object through its SharedPtr<T>, it can do so by calling
41  * readWrite() on the SharedPtr instance. readWrite() ensures that the
42  * SharedPtr<T> really does have its own private T object by cloning it if
43  * it is shared by using its clone() method. SharedPtr<T> instances handle
44  * management by reference counting their T objects. T objects that are
45  * referenced by no SharedPtr<T> instances get deleted automatically.
46  */
47 
48 // TODO (Travis Keep): Leave interface the same, but find a more efficient
49 // implementation that is easier to understand.
50 template<typename T>
51 class SharedPtr {
52 public:
53     /**
54      * Constructor. If there is a memory allocation error creating
55      * reference counter then this object will contain NULL, and adopted
56      * pointer will be freed. Note that when passing NULL or no argument to
57      * constructor, no memory allocation error can happen as NULL pointers
58      * are never reference counted.
59      */
ptr(adopted)60     explicit SharedPtr(T *adopted=NULL) : ptr(adopted), refPtr(NULL) {
61         if (ptr != NULL) {
62             refPtr = new AtomicInt();
63             if (refPtr == NULL) {
64                 delete ptr;
65                 ptr = NULL;
66             } else {
67                 refPtr->value = 1;
68             }
69         }
70     }
71 
72     /**
73      * Copy constructor.
74      */
SharedPtr(const SharedPtr<T> & other)75     SharedPtr(const SharedPtr<T> &other) :
76             ptr(other.ptr), refPtr(other.refPtr) {
77         if (refPtr != NULL) {
78             umtx_atomic_inc(&refPtr->value);
79         }
80     }
81 
82     /**
83      * assignment operator.
84      */
85     SharedPtr<T> &operator=(const SharedPtr<T> &other) {
86         if (ptr != other.ptr) {
87             SharedPtr<T> newValue(other);
88             swap(newValue);
89         }
90         return *this;
91     }
92 
93     /**
94      * Destructor.
95      */
~SharedPtr()96     ~SharedPtr() {
97         if (refPtr != NULL) {
98             if (umtx_atomic_dec(&refPtr->value) == 0) {
99                 delete ptr;
100                 delete refPtr;
101             }
102         }
103     }
104 
105     /**
106      * reset adopts a new pointer. On success, returns TRUE.
107      * On memory allocation error creating reference counter for adopted
108      * pointer, returns FALSE while leaving this instance unchanged.
109      */
reset(T * adopted)110     bool reset(T *adopted) {
111         SharedPtr<T> newValue(adopted);
112         if (adopted != NULL && newValue.ptr == NULL) {
113             // We couldn't allocate ref counter.
114             return FALSE;
115         }
116         swap(newValue);
117         return TRUE;
118     }
119 
120     /**
121      * reset makes this instance refer to no object.
122      */
reset()123     void reset() {
124         reset(NULL);
125     }
126 
127     /**
128      * count returns how many SharedPtr instances, including this one,
129      * refer to the T object. Used for testing. Clients need not use in
130      * practice.
131      */
count()132     int32_t count() const {
133         if (refPtr == NULL) {
134             return 0;
135         }
136         return umtx_loadAcquire(refPtr->value);
137     }
138 
139     /**
140      * Swaps this instance with other.
141      */
swap(SharedPtr<T> & other)142     void swap(SharedPtr<T> &other) {
143         T *tempPtr = other.ptr;
144         AtomicInt *tempRefPtr = other.refPtr;
145         other.ptr = ptr;
146         other.refPtr = refPtr;
147         ptr = tempPtr;
148         refPtr = tempRefPtr;
149     }
150 
151     const T *operator->() const {
152         return ptr;
153     }
154 
155     const T &operator*() const {
156         return *ptr;
157     }
158 
159     bool operator==(const T *other) const {
160         return ptr == other;
161     }
162 
163     bool operator!=(const T *other) const {
164         return ptr != other;
165     }
166 
167     /**
168      * readOnly gives const access to this instance's T object. If this
169      * instance refers to no object, returns NULL.
170      */
readOnly()171     const T *readOnly() const {
172         return ptr;
173     }
174 
175     /**
176      * readWrite returns a writable pointer to its T object copying it first
177      * using its clone() method if it is shared.
178      * On memory allocation error or if this instance refers to no object,
179      * this method returns NULL leaving this instance unchanged.
180      * <p>
181      * If readWrite() returns a non NULL pointer, it guarantees that this
182      * object holds the only reference to its T object enabling the caller to
183      * perform mutations using the returned pointer without affecting other
184      * SharedPtr objects. However, the non-constness of readWrite continues as
185      * long as the returned pointer is in scope. Therefore it is an API
186      * violation to call readWrite() on A; perform B = A; and then proceed to
187      * mutate A via its writeable pointer as that would be the same as setting
188      * B = A while A is changing. The returned pointer is guaranteed to be
189      * valid only while this object is in scope because this object maintains
190      * ownership of its T object. Therefore, callers must never attempt to
191      * delete the returned writeable pointer. The best practice with readWrite
192      * is this: callers should use the returned pointer from readWrite() only
193      * within the same scope as that call to readWrite, and that scope should
194      * be made as small as possible avoiding overlap with other operatios on
195      * this object.
196      */
readWrite()197     T *readWrite() {
198         int32_t refCount = count();
199         if (refCount <= 1) {
200             return ptr;
201         }
202         T *result = (T *) ptr->clone();
203         if (result == NULL) {
204             // Memory allocation error
205             return NULL;
206         }
207         if (!reset(result)) {
208             return NULL;
209         }
210         return ptr;
211     }
212 private:
213     T *ptr;
214     AtomicInt *refPtr;
215     // No heap allocation. Use only stack.
216     static void * U_EXPORT2 operator new(size_t size);
217     static void * U_EXPORT2 operator new[](size_t size);
218 #if U_HAVE_PLACEMENT_NEW
219     static void * U_EXPORT2 operator new(size_t, void *ptr);
220 #endif
221 };
222 
223 U_NAMESPACE_END
224 
225 #endif
226