Home | History | Annotate | Download | only in common
      1 /*
      2 ******************************************************************************
      3 * Copyright (C) 2015, International Business Machines Corporation and
      4 * others. All Rights Reserved.
      5 ******************************************************************************
      6 *
      7 * File UNIFIEDCACHE.H - The ICU Unified cache.
      8 ******************************************************************************
      9 */
     10 
     11 #ifndef __UNIFIED_CACHE_H__
     12 #define __UNIFIED_CACHE_H__
     13 
     14 #include "utypeinfo.h"  // for 'typeid' to work
     15 
     16 #include "unicode/uobject.h"
     17 #include "unicode/locid.h"
     18 #include "sharedobject.h"
     19 #include "unicode/unistr.h"
     20 #include "cstring.h"
     21 #include "ustr_imp.h"
     22 
     23 struct UHashtable;
     24 struct UHashElement;
     25 
     26 U_NAMESPACE_BEGIN
     27 
     28 class UnifiedCache;
     29 
     30 /**
     31  * A base class for all cache keys.
     32  */
     33 class U_COMMON_API CacheKeyBase : public UObject {
     34  public:
     35    CacheKeyBase() : fCreationStatus(U_ZERO_ERROR), fIsMaster(FALSE) {}
     36 
     37    /**
     38     * Copy constructor. Needed to support cloning.
     39     */
     40    CacheKeyBase(const CacheKeyBase &other)
     41            : UObject(other), fCreationStatus(other.fCreationStatus), fIsMaster(FALSE) { }
     42    virtual ~CacheKeyBase();
     43 
     44    /**
     45     * Returns the hash code for this object.
     46     */
     47    virtual int32_t hashCode() const = 0;
     48 
     49    /**
     50     * Clones this object polymorphically. Caller owns returned value.
     51     */
     52    virtual CacheKeyBase *clone() const = 0;
     53 
     54    /**
     55     * Equality operator.
     56     */
     57    virtual UBool operator == (const CacheKeyBase &other) const = 0;
     58 
     59    /**
     60     * Create a new object for this key. Called by cache on cache miss.
     61     * createObject must add a reference to the object it returns. Note
     62     * that getting an object from the cache and returning it without calling
     63     * removeRef on it satisfies this requirement. It can also return NULL
     64     * and set status to an error.
     65     *
     66     * @param creationContext the context in which the object is being
     67     *                        created. May be NULL.
     68     * @param status          Implementations can return a failure here.
     69     *                        In addition, implementations may return a
     70     *                        non NULL object and set a warning status.
     71     */
     72    virtual const SharedObject *createObject(
     73            const void *creationContext, UErrorCode &status) const = 0;
     74 
     75    /**
     76     * Writes a description of this key to buffer and returns buffer. Written
     77     * description is NULL terminated.
     78     */
     79    virtual char *writeDescription(char *buffer, int32_t bufSize) const = 0;
     80 
     81    /**
     82     * Inequality operator.
     83     */
     84    UBool operator != (const CacheKeyBase &other) const {
     85        return !(*this == other);
     86    }
     87  private:
     88    mutable UErrorCode fCreationStatus;
     89    mutable UBool fIsMaster;
     90    friend class UnifiedCache;
     91 };
     92 
     93 
     94 
     95 /**
     96  * Templated version of CacheKeyBase.
     97  * A key of type LocaleCacheKey<T> maps to a value of type T.
     98  */
     99 template<typename T>
    100 class CacheKey : public CacheKeyBase {
    101  public:
    102    virtual ~CacheKey() { }
    103    /**
    104     * The template parameter, T, determines the hash code returned.
    105     */
    106    virtual int32_t hashCode() const {
    107        const char *s = typeid(T).name();
    108        return ustr_hashCharsN(s, uprv_strlen(s));
    109    }
    110 
    111    /**
    112     * Use the value type, T,  as the description.
    113     */
    114    virtual char *writeDescription(char *buffer, int32_t bufLen) const {
    115        const char *s = typeid(T).name();
    116        uprv_strncpy(buffer, s, bufLen);
    117        buffer[bufLen - 1] = 0;
    118        return buffer;
    119    }
    120 
    121    /**
    122     * Two objects are equal if they are of the same type.
    123     */
    124    virtual UBool operator == (const CacheKeyBase &other) const {
    125        return typeid(*this) == typeid(other);
    126    }
    127 };
    128 
    129 /**
    130  * Cache key based on locale.
    131  * A key of type LocaleCacheKey<T> maps to a value of type T.
    132  */
    133 template<typename T>
    134 class LocaleCacheKey : public CacheKey<T> {
    135  protected:
    136    Locale   fLoc;
    137  public:
    138    LocaleCacheKey(const Locale &loc) : fLoc(loc) {};
    139    LocaleCacheKey(const LocaleCacheKey<T> &other)
    140            : CacheKey<T>(other), fLoc(other.fLoc) { }
    141    virtual ~LocaleCacheKey() { }
    142    virtual int32_t hashCode() const {
    143        return 37 *CacheKey<T>::hashCode() + fLoc.hashCode();
    144    }
    145    virtual UBool operator == (const CacheKeyBase &other) const {
    146        // reflexive
    147        if (this == &other) {
    148            return TRUE;
    149        }
    150        if (!CacheKey<T>::operator == (other)) {
    151            return FALSE;
    152        }
    153        // We know this and other are of same class because operator== on
    154        // CacheKey returned true.
    155        const LocaleCacheKey<T> *fOther =
    156                static_cast<const LocaleCacheKey<T> *>(&other);
    157        return fLoc == fOther->fLoc;
    158    }
    159    virtual CacheKeyBase *clone() const {
    160        return new LocaleCacheKey<T>(*this);
    161    }
    162    virtual const T *createObject(
    163            const void *creationContext, UErrorCode &status) const;
    164    /**
    165     * Use the locale id as the description.
    166     */
    167    virtual char *writeDescription(char *buffer, int32_t bufLen) const {
    168        const char *s = fLoc.getName();
    169        uprv_strncpy(buffer, s, bufLen);
    170        buffer[bufLen - 1] = 0;
    171        return buffer;
    172    }
    173 
    174 };
    175 
    176 /**
    177  * The unified cache. A singleton type.
    178  * Design doc here:
    179  * https://docs.google.com/document/d/1RwGQJs4N4tawNbf809iYDRCvXoMKqDJihxzYt1ysmd8/edit?usp=sharing
    180  */
    181 class U_COMMON_API UnifiedCache : public UnifiedCacheBase {
    182  public:
    183    /**
    184     * @internal
    185     * Do not call directly. Instead use UnifiedCache::getInstance() as
    186     * there should be only one UnifiedCache in an application.
    187     */
    188    UnifiedCache(UErrorCode &status);
    189 
    190    /**
    191     * Returns the cache instance.
    192     */
    193    static UnifiedCache *getInstance(UErrorCode &status);
    194 
    195    /**
    196     * Fetches a value from the cache by key. Equivalent to
    197     * get(key, NULL, ptr, status);
    198     */
    199    template<typename T>
    200    void get(
    201            const CacheKey<T>& key,
    202            const T *&ptr,
    203            UErrorCode &status) const {
    204        get(key, NULL, ptr, status);
    205    }
    206 
    207    /**
    208     * Fetches value from the cache by key.
    209     *
    210     * @param key             the cache key.
    211     * @param creationContext passed verbatim to createObject method of key
    212     * @param ptr             On entry, ptr must be NULL or be included if
    213     *                        the reference count of the object it points
    214     *                        to. On exit, ptr points to the fetched object
    215     *                        from the cache or is left unchanged on
    216     *                        failure. Caller must call removeRef on ptr
    217     *                        if set to a non NULL value.
    218     * @param status          Any error returned here. May be set to a
    219     *                        warning value even if ptr is set.
    220     */
    221    template<typename T>
    222    void get(
    223            const CacheKey<T>& key,
    224            const void *creationContext,
    225            const T *&ptr,
    226            UErrorCode &status) const {
    227        if (U_FAILURE(status)) {
    228            return;
    229        }
    230        UErrorCode creationStatus = U_ZERO_ERROR;
    231        const SharedObject *value = NULL;
    232        _get(key, value, creationContext, creationStatus);
    233        const T *tvalue = (const T *) value;
    234        if (U_SUCCESS(creationStatus)) {
    235            SharedObject::copyPtr(tvalue, ptr);
    236        }
    237        SharedObject::clearPtr(tvalue);
    238        // Take care not to overwrite a warning status passed in with
    239        // another warning or U_ZERO_ERROR.
    240        if (status == U_ZERO_ERROR || U_FAILURE(creationStatus)) {
    241            status = creationStatus;
    242        }
    243    }
    244 
    245 #ifdef UNIFIED_CACHE_DEBUG
    246    /**
    247     * Dumps the contents of this cache to standard error. Used for testing of
    248     * cache only.
    249     */
    250    void dumpContents() const;
    251 #endif
    252 
    253    /**
    254     * Convenience method to get a value of type T from cache for a
    255     * particular locale with creationContext == NULL.
    256     * @param loc    the locale
    257     * @param ptr    On entry, must be NULL or included in the ref count
    258     *               of the object to which it points.
    259     *               On exit, fetched value stored here or is left
    260     *               unchanged on failure. Caller must call removeRef on
    261     *               ptr if set to a non NULL value.
    262     * @param status Any error returned here. May be set to a
    263     *               warning value even if ptr is set.
    264     */
    265    template<typename T>
    266    static void getByLocale(
    267            const Locale &loc, const T *&ptr, UErrorCode &status) {
    268        const UnifiedCache *cache = getInstance(status);
    269        if (U_FAILURE(status)) {
    270            return;
    271        }
    272        cache->get(LocaleCacheKey<T>(loc), ptr, status);
    273    }
    274 
    275 #ifdef UNIFIED_CACHE_DEBUG
    276    /**
    277     * Dumps the cache contents to stderr. For testing only.
    278     */
    279    static void dump();
    280 #endif
    281 
    282    /**
    283     * Returns the number of keys in this cache. For testing only.
    284     */
    285    int32_t keyCount() const;
    286 
    287    /**
    288     * Removes any values from cache that are not referenced outside
    289     * the cache.
    290     */
    291    void flush() const;
    292 
    293    /**
    294     * Configures at what point evcition of unused entries will begin.
    295     * Eviction is triggered whenever the number of unused entries exeeds
    296     * BOTH count AND (number of in-use items) * (percentageOfInUseItems / 100).
    297     * Once the number of unused entries drops below one of these,
    298     * eviction ceases. Because eviction happens incrementally,
    299     * the actual unused entry count may exceed both these numbers
    300     * from time to time.
    301     *
    302     * A cache entry is defined as unused if it is not essential to guarantee
    303     * that for a given key X, the cache returns the same reference to the
    304     * same value as long as the client already holds a reference to that
    305     * value.
    306     *
    307     * If this method is never called, the default settings are 1000 and 100%.
    308     *
    309     * Although this method is thread-safe, it is designed to be called at
    310     * application startup. If it is called in the middle of execution, it
    311     * will have no immediate effect on the cache. However over time, the
    312     * cache will perform eviction slices in an attempt to honor the new
    313     * settings.
    314     *
    315     * If a client already holds references to many different unique values
    316     * in the cache such that the number of those unique values far exeeds
    317     * "count" then the cache may not be able to maintain this maximum.
    318     * However, if this happens, the cache still guarantees that the number of
    319     * unused entries will remain only a small percentage of the total cache
    320     * size.
    321     *
    322     * If the parameters passed are negative, setEvctionPolicy sets status to
    323     * U_ILLEGAL_ARGUMENT_ERROR.
    324     */
    325    void setEvictionPolicy(
    326            int32_t count, int32_t percentageOfInUseItems, UErrorCode &status);
    327 
    328 
    329    /**
    330     * Returns how many entries have been auto evicted during the lifetime
    331     * of this cache. This only includes auto evicted entries, not
    332     * entries evicted because of a call to flush().
    333     */
    334    int64_t autoEvictedCount() const;
    335 
    336    /**
    337     * Returns the unused entry count in this cache. For testing only,
    338     * Regular clients will not need this.
    339     */
    340    int32_t unusedCount() const;
    341 
    342    virtual void incrementItemsInUse() const;
    343    virtual void decrementItemsInUseWithLockingAndEviction() const;
    344    virtual void decrementItemsInUse() const;
    345    virtual ~UnifiedCache();
    346  private:
    347    UHashtable *fHashtable;
    348    mutable int32_t fEvictPos;
    349    mutable int32_t fItemsInUseCount;
    350    int32_t fMaxUnused;
    351    int32_t fMaxPercentageOfInUse;
    352    mutable int64_t fAutoEvictedCount;
    353    UnifiedCache(const UnifiedCache &other);
    354    UnifiedCache &operator=(const UnifiedCache &other);
    355    UBool _flush(UBool all) const;
    356    void _get(
    357            const CacheKeyBase &key,
    358            const SharedObject *&value,
    359            const void *creationContext,
    360            UErrorCode &status) const;
    361    UBool _poll(
    362            const CacheKeyBase &key,
    363            const SharedObject *&value,
    364            UErrorCode &status) const;
    365    void _putNew(
    366            const CacheKeyBase &key,
    367            const SharedObject *value,
    368            const UErrorCode creationStatus,
    369            UErrorCode &status) const;
    370    void _putIfAbsentAndGet(
    371            const CacheKeyBase &key,
    372            const SharedObject *&value,
    373            UErrorCode &status) const;
    374    const UHashElement *_nextElement() const;
    375    int32_t _computeCountOfItemsToEvict() const;
    376    void _runEvictionSlice() const;
    377    void _registerMaster(
    378         const CacheKeyBase *theKey, const SharedObject *value) const;
    379    void _put(
    380            const UHashElement *element,
    381            const SharedObject *value,
    382            const UErrorCode status) const;
    383 #ifdef UNIFIED_CACHE_DEBUG
    384    void _dumpContents() const;
    385 #endif
    386    static void copyPtr(const SharedObject *src, const SharedObject *&dest);
    387    static void clearPtr(const SharedObject *&ptr);
    388    static void _fetch(
    389            const UHashElement *element,
    390            const SharedObject *&value,
    391            UErrorCode &status);
    392    static UBool _inProgress(const UHashElement *element);
    393    static UBool _inProgress(
    394            const SharedObject *theValue, UErrorCode creationStatus);
    395    static UBool _isEvictable(const UHashElement *element);
    396 };
    397 
    398 U_NAMESPACE_END
    399 
    400 #endif
    401