| /* |
| ******************************************************************************* |
| * Copyright (C) 2014, International Business Machines Corporation and |
| * others. All Rights Reserved. |
| ******************************************************************************* |
| * |
| * File SHAREDPTR.H |
| ******************************************************************************* |
| */ |
| |
| #ifndef __SHARED_PTR_H__ |
| #define __SHARED_PTR_H__ |
| |
| #include "unicode/uobject.h" |
| #include "umutex.h" |
| #include "uassert.h" |
| |
| U_NAMESPACE_BEGIN |
| |
| // Wrap u_atomic_int32_t in a UMemory so that we allocate them in the same |
| // way we allocate all other ICU objects. |
| struct AtomicInt : public UMemory { |
| u_atomic_int32_t value; |
| }; |
| |
| /** |
| * SharedPtr are shared pointers that support copy-on-write sematics. |
| * SharedPtr makes the act of copying large objects cheap by deferring the |
| * cost of the copy to the first write operation after the copy. |
| * |
| * A SharedPtr<T> instance can refer to no object or an object of type T. |
| * T must have a clone() method that copies |
| * the object and returns a pointer to the copy. Copy and assignment of |
| * SharedPtr instances are cheap because they only involve copying or |
| * assigning the SharedPtr instance, not the T object which could be large. |
| * Although many SharedPtr<T> instances may refer to the same T object, |
| * clients can still assume that each SharedPtr<T> instance has its own |
| * private instance of T because each SharedPtr<T> instance offers only a |
| * const view of its T object through normal pointer operations. If a caller |
| * must change a T object through its SharedPtr<T>, it can do so by calling |
| * readWrite() on the SharedPtr instance. readWrite() ensures that the |
| * SharedPtr<T> really does have its own private T object by cloning it if |
| * it is shared by using its clone() method. SharedPtr<T> instances handle |
| * management by reference counting their T objects. T objects that are |
| * referenced by no SharedPtr<T> instances get deleted automatically. |
| */ |
| |
| // TODO (Travis Keep): Leave interface the same, but find a more efficient |
| // implementation that is easier to understand. |
| template<typename T> |
| class SharedPtr { |
| public: |
| /** |
| * Constructor. If there is a memory allocation error creating |
| * reference counter then this object will contain NULL, and adopted |
| * pointer will be freed. Note that when passing NULL or no argument to |
| * constructor, no memory allocation error can happen as NULL pointers |
| * are never reference counted. |
| */ |
| explicit SharedPtr(T *adopted=NULL) : ptr(adopted), refPtr(NULL) { |
| if (ptr != NULL) { |
| refPtr = new AtomicInt(); |
| if (refPtr == NULL) { |
| delete ptr; |
| ptr = NULL; |
| } else { |
| refPtr->value = 1; |
| } |
| } |
| } |
| |
| /** |
| * Copy constructor. |
| */ |
| SharedPtr(const SharedPtr<T> &other) : |
| ptr(other.ptr), refPtr(other.refPtr) { |
| if (refPtr != NULL) { |
| umtx_atomic_inc(&refPtr->value); |
| } |
| } |
| |
| /** |
| * assignment operator. |
| */ |
| SharedPtr<T> &operator=(const SharedPtr<T> &other) { |
| if (ptr != other.ptr) { |
| SharedPtr<T> newValue(other); |
| swap(newValue); |
| } |
| return *this; |
| } |
| |
| /** |
| * Destructor. |
| */ |
| ~SharedPtr() { |
| if (refPtr != NULL) { |
| if (umtx_atomic_dec(&refPtr->value) == 0) { |
| delete ptr; |
| delete refPtr; |
| } |
| } |
| } |
| |
| /** |
| * reset adopts a new pointer. On success, returns TRUE. |
| * On memory allocation error creating reference counter for adopted |
| * pointer, returns FALSE while leaving this instance unchanged. |
| */ |
| bool reset(T *adopted) { |
| SharedPtr<T> newValue(adopted); |
| if (adopted != NULL && newValue.ptr == NULL) { |
| // We couldn't allocate ref counter. |
| return FALSE; |
| } |
| swap(newValue); |
| return TRUE; |
| } |
| |
| /** |
| * reset makes this instance refer to no object. |
| */ |
| void reset() { |
| reset(NULL); |
| } |
| |
| /** |
| * count returns how many SharedPtr instances, including this one, |
| * refer to the T object. Used for testing. Clients need not use in |
| * practice. |
| */ |
| int32_t count() const { |
| if (refPtr == NULL) { |
| return 0; |
| } |
| return umtx_loadAcquire(refPtr->value); |
| } |
| |
| /** |
| * Swaps this instance with other. |
| */ |
| void swap(SharedPtr<T> &other) { |
| T *tempPtr = other.ptr; |
| AtomicInt *tempRefPtr = other.refPtr; |
| other.ptr = ptr; |
| other.refPtr = refPtr; |
| ptr = tempPtr; |
| refPtr = tempRefPtr; |
| } |
| |
| const T *operator->() const { |
| return ptr; |
| } |
| |
| const T &operator*() const { |
| return *ptr; |
| } |
| |
| bool operator==(const T *other) const { |
| return ptr == other; |
| } |
| |
| bool operator!=(const T *other) const { |
| return ptr != other; |
| } |
| |
| /** |
| * readOnly gives const access to this instance's T object. If this |
| * instance refers to no object, returns NULL. |
| */ |
| const T *readOnly() const { |
| return ptr; |
| } |
| |
| /** |
| * readWrite returns a writable pointer to its T object copying it first |
| * using its clone() method if it is shared. |
| * On memory allocation error or if this instance refers to no object, |
| * this method returns NULL leaving this instance unchanged. |
| * <p> |
| * If readWrite() returns a non NULL pointer, it guarantees that this |
| * object holds the only reference to its T object enabling the caller to |
| * perform mutations using the returned pointer without affecting other |
| * SharedPtr objects. However, the non-constness of readWrite continues as |
| * long as the returned pointer is in scope. Therefore it is an API |
| * violation to call readWrite() on A; perform B = A; and then proceed to |
| * mutate A via its writeable pointer as that would be the same as setting |
| * B = A while A is changing. The returned pointer is guaranteed to be |
| * valid only while this object is in scope because this object maintains |
| * ownership of its T object. Therefore, callers must never attempt to |
| * delete the returned writeable pointer. The best practice with readWrite |
| * is this: callers should use the returned pointer from readWrite() only |
| * within the same scope as that call to readWrite, and that scope should |
| * be made as small as possible avoiding overlap with other operatios on |
| * this object. |
| */ |
| T *readWrite() { |
| int32_t refCount = count(); |
| if (refCount <= 1) { |
| return ptr; |
| } |
| T *result = (T *) ptr->clone(); |
| if (result == NULL) { |
| // Memory allocation error |
| return NULL; |
| } |
| if (!reset(result)) { |
| return NULL; |
| } |
| return ptr; |
| } |
| private: |
| T *ptr; |
| AtomicInt *refPtr; |
| // No heap allocation. Use only stack. |
| static void * U_EXPORT2 operator new(size_t size); |
| static void * U_EXPORT2 operator new[](size_t size); |
| #if U_HAVE_PLACEMENT_NEW |
| static void * U_EXPORT2 operator new(size_t, void *ptr); |
| #endif |
| }; |
| |
| U_NAMESPACE_END |
| |
| #endif |