Home | History | Annotate | Download | only in common
      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