]> git.saurik.com Git - apple/icu.git/blob - icuSources/common/sharedptr.h
ICU-531.48.tar.gz
[apple/icu.git] / icuSources / common / sharedptr.h
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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