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