// © 2016 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
/*
******************************************************************************
* Copyright (C) 2015-2016, International Business Machines
* Corporation and others. All Rights Reserved.
******************************************************************************
* sharedobject.h
*/
#ifndef __SHAREDOBJECT_H__
#define __SHAREDOBJECT_H__
#include "unicode/uobject.h"
#include "umutex.h"
U_NAMESPACE_BEGIN
/**
* Base class for unified cache exposing enough methods to SharedObject
* instances to allow their addRef() and removeRef() methods to
* update cache metrics. No other part of ICU, except for SharedObject,
* should directly call the methods of this base class.
*/
class U_COMMON_API UnifiedCacheBase : public UObject {
public:
UnifiedCacheBase() { }
/**
* Called by addRefWhileHoldingCacheLock() when the hard reference count
* of its instance goes from 0 to 1.
*/
virtual void incrementItemsInUse() const = 0;
/**
* Called by removeRef() when the hard reference count of its instance
* drops from 1 to 0.
*/
virtual void decrementItemsInUseWithLockingAndEviction() const = 0;
/**
* Called by removeRefWhileHoldingCacheLock() when the hard reference
* count of its instance drops from 1 to 0.
*/
virtual void decrementItemsInUse() const = 0;
virtual ~UnifiedCacheBase();
private:
UnifiedCacheBase(const UnifiedCacheBase &);
UnifiedCacheBase &operator=(const UnifiedCacheBase &);
};
/**
* Base class for shared, reference-counted, auto-deleted objects.
* Subclasses can be immutable.
* If they are mutable, then they must implement their copy constructor
* so that copyOnWrite() works.
*
* Either stack-allocate, use LocalPointer, or use addRef()/removeRef().
* Sharing requires reference-counting.
*/
class U_COMMON_API SharedObject : public UObject {
public:
/** Initializes totalRefCount, softRefCount to 0. */
SharedObject() :
totalRefCount(0),
softRefCount(0),
hardRefCount(0),
cachePtr(NULL) {}
/** Initializes totalRefCount, softRefCount to 0. */
SharedObject(const SharedObject &other) :
UObject(other),
totalRefCount(0),
softRefCount(0),
hardRefCount(0),
cachePtr(NULL) {}
virtual ~SharedObject();
/**
* Increments the number of references to this object. Thread-safe.
*/
void addRef() const { addRef(FALSE); }
/**
* Increments the number of references to this object.
* Must be called only from within the internals of UnifiedCache and
* only while the cache global mutex is held.
*/
void addRefWhileHoldingCacheLock() const { addRef(TRUE); }
/**
* Increments the number of soft references to this object.
* Must be called only from within the internals of UnifiedCache and
* only while the cache global mutex is held.
*/
void addSoftRef() const;
/**
* Decrements the number of references to this object. Thread-safe.
*/
void removeRef() const { removeRef(FALSE); }
/**
* Decrements the number of references to this object.
* Must be called only from within the internals of UnifiedCache and
* only while the cache global mutex is held.
*/
void removeRefWhileHoldingCacheLock() const { removeRef(TRUE); }
/**
* Decrements the number of soft references to this object.
* Must be called only from within the internals of UnifiedCache and
* only while the cache global mutex is held.
*/
void removeSoftRef() const;
/**
* Returns the reference counter including soft references.
* Uses a memory barrier.
*/
int32_t getRefCount() const;
/**
* Returns the count of soft references only.
* Must be called only from within the internals of UnifiedCache and
* only while the cache global mutex is held.
*/
int32_t getSoftRefCount() const { return softRefCount; }
/**
* Returns the count of hard references only. Uses a memory barrier.
* Used for testing the cache. Regular clients won't need this.
*/
int32_t getHardRefCount() const;
/**
* If noHardReferences() == TRUE then this object has no hard references.
* Must be called only from within the internals of UnifiedCache.
*/
inline UBool noHardReferences() const { return getHardRefCount() == 0; }
/**
* If hasHardReferences() == TRUE then this object has hard references.
* Must be called only from within the internals of UnifiedCache.
*/
inline UBool hasHardReferences() const { return getHardRefCount() != 0; }
/**
* If noSoftReferences() == TRUE then this object has no soft references.
* Must be called only from within the internals of UnifiedCache and
* only while the cache global mutex is held.
*/
UBool noSoftReferences() const { return (softRefCount == 0); }
/**
* Deletes this object if it has no references or soft references.
*/
void deleteIfZeroRefCount() const;
/**
* @internal For UnifedCache use only to register this object with itself.
* Must be called before this object is exposed to multiple threads.
*/
void registerWithCache(const UnifiedCacheBase *ptr) const {
cachePtr = ptr;
}
/**
* Returns a writable version of ptr.
* If there is exactly one owner, then ptr itself is returned as a
* non-const pointer.
* If there are multiple owners, then ptr is replaced with a
* copy-constructed clone,
* and that is returned.
* Returns NULL if cloning failed.
*
* T must be a subclass of SharedObject.
*/
template<typename T>
static T *copyOnWrite(const T *&ptr) {
const T *p = ptr;
if(p->getRefCount() <= 1) { return const_cast<T *>(p); }
T *p2 = new T(*p);
if(p2 == NULL) { return NULL; }
p->removeRef();
ptr = p2;
p2->addRef();
return p2;
}
/**
* Makes dest an owner of the object pointed to by src while adjusting
* reference counts and deleting the previous object dest pointed to
* if necessary. Before this call is made, dest must either be NULL or
* be included in the reference count of the object it points to.
*
* T must be a subclass of SharedObject.
*/
template<typename T>
static void copyPtr(const T *src, const T *&dest) {
if(src != dest) {
if(dest != NULL) { dest->removeRef(); }
dest = src;
if(src != NULL) { src->addRef(); }
}
}
/**
* Equivalent to copyPtr(NULL, dest).
*/
template<typename T>
static void clearPtr(const T *&ptr) {
if (ptr != NULL) {
ptr->removeRef();
ptr = NULL;
}
}
private:
mutable u_atomic_int32_t totalRefCount;
// Any thread modifying softRefCount must hold the global cache mutex
mutable int32_t softRefCount;
mutable u_atomic_int32_t hardRefCount;
mutable const UnifiedCacheBase *cachePtr;
void addRef(UBool withCacheLock) const;
void removeRef(UBool withCacheLock) const;
};
U_NAMESPACE_END
#endif