unifiedcache.h 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556
  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. #ifndef __UNIFIED_CACHE_H__
  13. #define __UNIFIED_CACHE_H__
  14. #include "utypeinfo.h" // for 'typeid' to work
  15. #include "unicode/uobject.h"
  16. #include "unicode/locid.h"
  17. #include "sharedobject.h"
  18. #include "unicode/unistr.h"
  19. #include "cstring.h"
  20. #include "ustr_imp.h"
  21. struct UHashtable;
  22. struct UHashElement;
  23. U_NAMESPACE_BEGIN
  24. class UnifiedCache;
  25. /**
  26. * A base class for all cache keys.
  27. */
  28. class U_COMMON_API CacheKeyBase : public UObject {
  29. public:
  30. CacheKeyBase() : fCreationStatus(U_ZERO_ERROR), fIsPrimary(false) {}
  31. /**
  32. * Copy constructor. Needed to support cloning.
  33. */
  34. CacheKeyBase(const CacheKeyBase &other)
  35. : UObject(other), fCreationStatus(other.fCreationStatus), fIsPrimary(false) { }
  36. virtual ~CacheKeyBase();
  37. /**
  38. * Returns the hash code for this object.
  39. */
  40. virtual int32_t hashCode() const = 0;
  41. /**
  42. * Clones this object polymorphically. Caller owns returned value.
  43. */
  44. virtual CacheKeyBase *clone() const = 0;
  45. /**
  46. * Create a new object for this key. Called by cache on cache miss.
  47. * createObject must add a reference to the object it returns. Note
  48. * that getting an object from the cache and returning it without calling
  49. * removeRef on it satisfies this requirement. It can also return nullptr
  50. * and set status to an error.
  51. *
  52. * @param creationContext the context in which the object is being
  53. * created. May be nullptr.
  54. * @param status Implementations can return a failure here.
  55. * In addition, implementations may return a
  56. * non nullptr object and set a warning status.
  57. */
  58. virtual const SharedObject *createObject(
  59. const void *creationContext, UErrorCode &status) const = 0;
  60. /**
  61. * Writes a description of this key to buffer and returns buffer. Written
  62. * description is nullptr terminated.
  63. */
  64. virtual char *writeDescription(char *buffer, int32_t bufSize) const = 0;
  65. friend inline bool operator==(const CacheKeyBase& lhs,
  66. const CacheKeyBase& rhs) {
  67. return lhs.equals(rhs);
  68. }
  69. friend inline bool operator!=(const CacheKeyBase& lhs,
  70. const CacheKeyBase& rhs) {
  71. return !lhs.equals(rhs);
  72. }
  73. protected:
  74. virtual bool equals(const CacheKeyBase& other) const = 0;
  75. private:
  76. mutable UErrorCode fCreationStatus;
  77. mutable UBool fIsPrimary;
  78. friend class UnifiedCache;
  79. };
  80. /**
  81. * Templated version of CacheKeyBase.
  82. * A key of type LocaleCacheKey<T> maps to a value of type T.
  83. */
  84. template<typename T>
  85. class CacheKey : public CacheKeyBase {
  86. public:
  87. virtual ~CacheKey() { }
  88. /**
  89. * The template parameter, T, determines the hash code returned.
  90. */
  91. virtual int32_t hashCode() const override {
  92. const char *s = typeid(T).name();
  93. return ustr_hashCharsN(s, static_cast<int32_t>(uprv_strlen(s)));
  94. }
  95. /**
  96. * Use the value type, T, as the description.
  97. */
  98. virtual char *writeDescription(char *buffer, int32_t bufLen) const override {
  99. const char *s = typeid(T).name();
  100. uprv_strncpy(buffer, s, bufLen);
  101. buffer[bufLen - 1] = 0;
  102. return buffer;
  103. }
  104. protected:
  105. /**
  106. * Two objects are equal if they are of the same type.
  107. */
  108. virtual bool equals(const CacheKeyBase &other) const override {
  109. return this == &other || typeid(*this) == typeid(other);
  110. }
  111. };
  112. /**
  113. * Cache key based on locale.
  114. * A key of type LocaleCacheKey<T> maps to a value of type T.
  115. */
  116. template<typename T>
  117. class LocaleCacheKey : public CacheKey<T> {
  118. protected:
  119. Locale fLoc;
  120. virtual bool equals(const CacheKeyBase &other) const override {
  121. if (!CacheKey<T>::equals(other)) {
  122. return false;
  123. }
  124. // We know this and other are of same class because equals() on
  125. // CacheKey returned true.
  126. return operator==(static_cast<const LocaleCacheKey<T> &>(other));
  127. }
  128. public:
  129. LocaleCacheKey(const Locale &loc) : fLoc(loc) {}
  130. LocaleCacheKey(const LocaleCacheKey<T> &other)
  131. : CacheKey<T>(other), fLoc(other.fLoc) { }
  132. virtual ~LocaleCacheKey() { }
  133. virtual int32_t hashCode() const override {
  134. return (int32_t)(37u * (uint32_t)CacheKey<T>::hashCode() + (uint32_t)fLoc.hashCode());
  135. }
  136. inline bool operator == (const LocaleCacheKey<T> &other) const {
  137. return fLoc == other.fLoc;
  138. }
  139. virtual CacheKeyBase *clone() const override {
  140. return new LocaleCacheKey<T>(*this);
  141. }
  142. virtual const T *createObject(
  143. const void *creationContext, UErrorCode &status) const override;
  144. /**
  145. * Use the locale id as the description.
  146. */
  147. virtual char *writeDescription(char *buffer, int32_t bufLen) const override {
  148. const char *s = fLoc.getName();
  149. uprv_strncpy(buffer, s, bufLen);
  150. buffer[bufLen - 1] = 0;
  151. return buffer;
  152. }
  153. };
  154. /**
  155. * The unified cache. A singleton type.
  156. * Design doc here:
  157. * https://docs.google.com/document/d/1RwGQJs4N4tawNbf809iYDRCvXoMKqDJihxzYt1ysmd8/edit?usp=sharing
  158. */
  159. class U_COMMON_API UnifiedCache : public UnifiedCacheBase {
  160. public:
  161. /**
  162. * @internal
  163. * Do not call directly. Instead use UnifiedCache::getInstance() as
  164. * there should be only one UnifiedCache in an application.
  165. */
  166. UnifiedCache(UErrorCode &status);
  167. /**
  168. * Return a pointer to the global cache instance.
  169. */
  170. static UnifiedCache *getInstance(UErrorCode &status);
  171. /**
  172. * Fetches a value from the cache by key. Equivalent to
  173. * get(key, nullptr, ptr, status);
  174. */
  175. template<typename T>
  176. void get(
  177. const CacheKey<T>& key,
  178. const T *&ptr,
  179. UErrorCode &status) const {
  180. get(key, nullptr, ptr, status);
  181. }
  182. /**
  183. * Fetches value from the cache by key.
  184. *
  185. * @param key the cache key.
  186. * @param creationContext passed verbatim to createObject method of key
  187. * @param ptr On entry, ptr must be nullptr or be included if
  188. * the reference count of the object it points
  189. * to. On exit, ptr points to the fetched object
  190. * from the cache or is left unchanged on
  191. * failure. Caller must call removeRef on ptr
  192. * if set to a non nullptr value.
  193. * @param status Any error returned here. May be set to a
  194. * warning value even if ptr is set.
  195. */
  196. template<typename T>
  197. void get(
  198. const CacheKey<T>& key,
  199. const void *creationContext,
  200. const T *&ptr,
  201. UErrorCode &status) const {
  202. if (U_FAILURE(status)) {
  203. return;
  204. }
  205. UErrorCode creationStatus = U_ZERO_ERROR;
  206. const SharedObject *value = nullptr;
  207. _get(key, value, creationContext, creationStatus);
  208. const T *tvalue = (const T *) value;
  209. if (U_SUCCESS(creationStatus)) {
  210. SharedObject::copyPtr(tvalue, ptr);
  211. }
  212. SharedObject::clearPtr(tvalue);
  213. // Take care not to overwrite a warning status passed in with
  214. // another warning or U_ZERO_ERROR.
  215. if (status == U_ZERO_ERROR || U_FAILURE(creationStatus)) {
  216. status = creationStatus;
  217. }
  218. }
  219. #ifdef UNIFIED_CACHE_DEBUG
  220. /**
  221. * Dumps the contents of this cache to standard error. Used for testing of
  222. * cache only.
  223. */
  224. void dumpContents() const;
  225. #endif
  226. /**
  227. * Convenience method to get a value of type T from cache for a
  228. * particular locale with creationContext == nullptr.
  229. * @param loc the locale
  230. * @param ptr On entry, must be nullptr or included in the ref count
  231. * of the object to which it points.
  232. * On exit, fetched value stored here or is left
  233. * unchanged on failure. Caller must call removeRef on
  234. * ptr if set to a non nullptr value.
  235. * @param status Any error returned here. May be set to a
  236. * warning value even if ptr is set.
  237. */
  238. template<typename T>
  239. static void getByLocale(
  240. const Locale &loc, const T *&ptr, UErrorCode &status) {
  241. const UnifiedCache *cache = getInstance(status);
  242. if (U_FAILURE(status)) {
  243. return;
  244. }
  245. cache->get(LocaleCacheKey<T>(loc), ptr, status);
  246. }
  247. #ifdef UNIFIED_CACHE_DEBUG
  248. /**
  249. * Dumps the cache contents to stderr. For testing only.
  250. */
  251. static void dump();
  252. #endif
  253. /**
  254. * Returns the number of keys in this cache. For testing only.
  255. */
  256. int32_t keyCount() const;
  257. /**
  258. * Removes any values from cache that are not referenced outside
  259. * the cache.
  260. */
  261. void flush() const;
  262. /**
  263. * Configures at what point eviction of unused entries will begin.
  264. * Eviction is triggered whenever the number of evictable keys exceeds
  265. * BOTH count AND (number of in-use items) * (percentageOfInUseItems / 100).
  266. * Once the number of unused entries drops below one of these,
  267. * eviction ceases. Because eviction happens incrementally,
  268. * the actual unused entry count may exceed both these numbers
  269. * from time to time.
  270. *
  271. * A cache entry is defined as unused if it is not essential to guarantee
  272. * that for a given key X, the cache returns the same reference to the
  273. * same value as long as the client already holds a reference to that
  274. * value.
  275. *
  276. * If this method is never called, the default settings are 1000 and 100%.
  277. *
  278. * Although this method is thread-safe, it is designed to be called at
  279. * application startup. If it is called in the middle of execution, it
  280. * will have no immediate effect on the cache. However over time, the
  281. * cache will perform eviction slices in an attempt to honor the new
  282. * settings.
  283. *
  284. * If a client already holds references to many different unique values
  285. * in the cache such that the number of those unique values far exceeds
  286. * "count" then the cache may not be able to maintain this maximum.
  287. * However, if this happens, the cache still guarantees that the number of
  288. * unused entries will remain only a small percentage of the total cache
  289. * size.
  290. *
  291. * If the parameters passed are negative, setEvctionPolicy sets status to
  292. * U_ILLEGAL_ARGUMENT_ERROR.
  293. */
  294. void setEvictionPolicy(
  295. int32_t count, int32_t percentageOfInUseItems, UErrorCode &status);
  296. /**
  297. * Returns how many entries have been auto evicted during the lifetime
  298. * of this cache. This only includes auto evicted entries, not
  299. * entries evicted because of a call to flush().
  300. */
  301. int64_t autoEvictedCount() const;
  302. /**
  303. * Returns the unused entry count in this cache. For testing only,
  304. * Regular clients will not need this.
  305. */
  306. int32_t unusedCount() const;
  307. virtual void handleUnreferencedObject() const override;
  308. virtual ~UnifiedCache();
  309. private:
  310. UHashtable *fHashtable;
  311. mutable int32_t fEvictPos;
  312. mutable int32_t fNumValuesTotal;
  313. mutable int32_t fNumValuesInUse;
  314. int32_t fMaxUnused;
  315. int32_t fMaxPercentageOfInUse;
  316. mutable int64_t fAutoEvictedCount;
  317. SharedObject *fNoValue;
  318. UnifiedCache(const UnifiedCache &other) = delete;
  319. UnifiedCache &operator=(const UnifiedCache &other) = delete;
  320. /**
  321. * Flushes the contents of the cache. If cache values hold references to other
  322. * cache values then _flush should be called in a loop until it returns false.
  323. *
  324. * On entry, gCacheMutex must be held.
  325. * On exit, those values with are evictable are flushed.
  326. *
  327. * @param all if false flush evictable items only, which are those with no external
  328. * references, plus those that can be safely recreated.<br>
  329. * if true, flush all elements. Any values (sharedObjects) with remaining
  330. * hard (external) references are not deleted, but are detached from
  331. * the cache, so that a subsequent removeRefs can delete them.
  332. * _flush is not thread safe when all is true.
  333. * @return true if any value in cache was flushed or false otherwise.
  334. */
  335. UBool _flush(UBool all) const;
  336. /**
  337. * Gets value out of cache.
  338. * On entry. gCacheMutex must not be held. value must be nullptr. status
  339. * must be U_ZERO_ERROR.
  340. * On exit. value and status set to what is in cache at key or on cache
  341. * miss the key's createObject() is called and value and status are set to
  342. * the result of that. In this latter case, best effort is made to add the
  343. * value and status to the cache. If createObject() fails to create a value,
  344. * fNoValue is stored in cache, and value is set to nullptr. Caller must call
  345. * removeRef on value if non nullptr.
  346. */
  347. void _get(
  348. const CacheKeyBase &key,
  349. const SharedObject *&value,
  350. const void *creationContext,
  351. UErrorCode &status) const;
  352. /**
  353. * Attempts to fetch value and status for key from cache.
  354. * On entry, gCacheMutex must not be held value must be nullptr and status must
  355. * be U_ZERO_ERROR.
  356. * On exit, either returns false (In this
  357. * case caller should try to create the object) or returns true with value
  358. * pointing to the fetched value and status set to fetched status. When
  359. * false is returned status may be set to failure if an in progress hash
  360. * entry could not be made but value will remain unchanged. When true is
  361. * returned, caller must call removeRef() on value.
  362. */
  363. UBool _poll(
  364. const CacheKeyBase &key,
  365. const SharedObject *&value,
  366. UErrorCode &status) const;
  367. /**
  368. * Places a new value and creationStatus in the cache for the given key.
  369. * On entry, gCacheMutex must be held. key must not exist in the cache.
  370. * On exit, value and creation status placed under key. Soft reference added
  371. * to value on successful add. On error sets status.
  372. */
  373. void _putNew(
  374. const CacheKeyBase &key,
  375. const SharedObject *value,
  376. const UErrorCode creationStatus,
  377. UErrorCode &status) const;
  378. /**
  379. * Places value and status at key if there is no value at key or if cache
  380. * entry for key is in progress. Otherwise, it leaves the current value and
  381. * status there.
  382. *
  383. * On entry. gCacheMutex must not be held. Value must be
  384. * included in the reference count of the object to which it points.
  385. *
  386. * On exit, value and status are changed to what was already in the cache if
  387. * something was there and not in progress. Otherwise, value and status are left
  388. * unchanged in which case they are placed in the cache on a best-effort basis.
  389. * Caller must call removeRef() on value.
  390. */
  391. void _putIfAbsentAndGet(
  392. const CacheKeyBase &key,
  393. const SharedObject *&value,
  394. UErrorCode &status) const;
  395. /**
  396. * Returns the next element in the cache round robin style.
  397. * Returns nullptr if the cache is empty.
  398. * On entry, gCacheMutex must be held.
  399. */
  400. const UHashElement *_nextElement() const;
  401. /**
  402. * Return the number of cache items that would need to be evicted
  403. * to bring usage into conformance with eviction policy.
  404. *
  405. * An item corresponds to an entry in the hash table, a hash table element.
  406. *
  407. * On entry, gCacheMutex must be held.
  408. */
  409. int32_t _computeCountOfItemsToEvict() const;
  410. /**
  411. * Run an eviction slice.
  412. * On entry, gCacheMutex must be held.
  413. * _runEvictionSlice runs a slice of the evict pipeline by examining the next
  414. * 10 entries in the cache round robin style evicting them if they are eligible.
  415. */
  416. void _runEvictionSlice() const;
  417. /**
  418. * Register a primary cache entry. A primary key is the first key to create
  419. * a given SharedObject value. Subsequent keys whose create function
  420. * produce references to an already existing SharedObject are not primary -
  421. * they can be evicted and subsequently recreated.
  422. *
  423. * On entry, gCacheMutex must be held.
  424. * On exit, items in use count incremented, entry is marked as a primary
  425. * entry, and value registered with cache so that subsequent calls to
  426. * addRef() and removeRef() on it correctly interact with the cache.
  427. */
  428. void _registerPrimary(const CacheKeyBase *theKey, const SharedObject *value) const;
  429. /**
  430. * Store a value and creation error status in given hash entry.
  431. * On entry, gCacheMutex must be held. Hash entry element must be in progress.
  432. * value must be non nullptr.
  433. * On Exit, soft reference added to value. value and status stored in hash
  434. * entry. Soft reference removed from previous stored value. Waiting
  435. * threads notified.
  436. */
  437. void _put(
  438. const UHashElement *element,
  439. const SharedObject *value,
  440. const UErrorCode status) const;
  441. /**
  442. * Remove a soft reference, and delete the SharedObject if no references remain.
  443. * To be used from within the UnifiedCache implementation only.
  444. * gCacheMutex must be held by caller.
  445. * @param value the SharedObject to be acted on.
  446. */
  447. void removeSoftRef(const SharedObject *value) const;
  448. /**
  449. * Increment the hard reference count of the given SharedObject.
  450. * gCacheMutex must be held by the caller.
  451. * Update numValuesEvictable on transitions between zero and one reference.
  452. *
  453. * @param value The SharedObject to be referenced.
  454. * @return the hard reference count after the addition.
  455. */
  456. int32_t addHardRef(const SharedObject *value) const;
  457. /**
  458. * Decrement the hard reference count of the given SharedObject.
  459. * gCacheMutex must be held by the caller.
  460. * Update numValuesEvictable on transitions between one and zero reference.
  461. *
  462. * @param value The SharedObject to be referenced.
  463. * @return the hard reference count after the removal.
  464. */
  465. int32_t removeHardRef(const SharedObject *value) const;
  466. #ifdef UNIFIED_CACHE_DEBUG
  467. void _dumpContents() const;
  468. #endif
  469. /**
  470. * Fetch value and error code from a particular hash entry.
  471. * On entry, gCacheMutex must be held. value must be either nullptr or must be
  472. * included in the ref count of the object to which it points.
  473. * On exit, value and status set to what is in the hash entry. Caller must
  474. * eventually call removeRef on value.
  475. * If hash entry is in progress, value will be set to gNoValue and status will
  476. * be set to U_ZERO_ERROR.
  477. */
  478. void _fetch(const UHashElement *element, const SharedObject *&value,
  479. UErrorCode &status) const;
  480. /**
  481. * Determine if given hash entry is in progress.
  482. * On entry, gCacheMutex must be held.
  483. */
  484. UBool _inProgress(const UHashElement *element) const;
  485. /**
  486. * Determine if given hash entry is in progress.
  487. * On entry, gCacheMutex must be held.
  488. */
  489. UBool _inProgress(const SharedObject *theValue, UErrorCode creationStatus) const;
  490. /**
  491. * Determine if given hash entry is eligible for eviction.
  492. * On entry, gCacheMutex must be held.
  493. */
  494. UBool _isEvictable(const UHashElement *element) const;
  495. };
  496. U_NAMESPACE_END
  497. #endif