/* ******************************************************************************* * 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 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 instances may refer to the same T object, * clients can still assume that each SharedPtr instance has its own * private instance of T because each SharedPtr 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, it can do so by calling * readWrite() on the SharedPtr instance. readWrite() ensures that the * SharedPtr really does have its own private T object by cloning it if * it is shared by using its clone() method. SharedPtr instances handle * management by reference counting their T objects. T objects that are * referenced by no SharedPtr instances get deleted automatically. */ // TODO (Travis Keep): Leave interface the same, but find a more efficient // implementation that is easier to understand. template 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 &other) : ptr(other.ptr), refPtr(other.refPtr) { if (refPtr != NULL) { umtx_atomic_inc(&refPtr->value); } } /** * assignment operator. */ SharedPtr &operator=(const SharedPtr &other) { if (ptr != other.ptr) { SharedPtr 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 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 &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. *

* 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