| /* | 
 | ****************************************************************************** | 
 | * Copyright (C) 2015, 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 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 |