f56a9be8fd
X-SVN-Rev: 34891
226 lines
7.3 KiB
C++
226 lines
7.3 KiB
C++
/*
|
|
*******************************************************************************
|
|
* 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
|