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 226