+/*
+*******************************************************************************
+* 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