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
18U_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.
22struct 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.
50template<typename T>
51class SharedPtr {
52public:
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    }
212private:
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
223U_NAMESPACE_END
224
225#endif
226