// © 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 static T *copyOnWrite(const T *&ptr) { const T *p = ptr; if(p->getRefCount() <= 1) { return const_cast(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 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 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