X-Git-Url: https://git.saurik.com/apple/icu.git/blobdiff_plain/efa1e6592fb03ce23b15276b2b91d885a3ee7da5..57a6839dcb3bba09e8228b822b290604668416fe:/icuSources/common/sharedptr.h?ds=inline diff --git a/icuSources/common/sharedptr.h b/icuSources/common/sharedptr.h new file mode 100644 index 00000000..31d62aae --- /dev/null +++ b/icuSources/common/sharedptr.h @@ -0,0 +1,225 @@ +/* +******************************************************************************* +* 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