Line data Source code
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 0 : CacheKeyBase() : fCreationStatus(U_ZERO_ERROR), fIsMaster(FALSE) {}
38 :
39 : /**
40 : * Copy constructor. Needed to support cloning.
41 : */
42 0 : CacheKeyBase(const CacheKeyBase &other)
43 0 : : 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 0 : class CacheKey : public CacheKeyBase {
103 : public:
104 0 : virtual ~CacheKey() { }
105 : /**
106 : * The template parameter, T, determines the hash code returned.
107 : */
108 0 : virtual int32_t hashCode() const {
109 0 : const char *s = typeid(T).name();
110 0 : return ustr_hashCharsN(s, uprv_strlen(s));
111 : }
112 :
113 : /**
114 : * Use the value type, T, as the description.
115 : */
116 0 : virtual char *writeDescription(char *buffer, int32_t bufLen) const {
117 0 : const char *s = typeid(T).name();
118 0 : uprv_strncpy(buffer, s, bufLen);
119 0 : buffer[bufLen - 1] = 0;
120 0 : return buffer;
121 : }
122 :
123 : /**
124 : * Two objects are equal if they are of the same type.
125 : */
126 0 : virtual UBool operator == (const CacheKeyBase &other) const {
127 0 : 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 0 : LocaleCacheKey(const Locale &loc) : fLoc(loc) {};
141 0 : LocaleCacheKey(const LocaleCacheKey<T> &other)
142 0 : : CacheKey<T>(other), fLoc(other.fLoc) { }
143 0 : virtual ~LocaleCacheKey() { }
144 0 : virtual int32_t hashCode() const {
145 0 : return (int32_t)(37u * (uint32_t)CacheKey<T>::hashCode() + (uint32_t)fLoc.hashCode());
146 : }
147 0 : virtual UBool operator == (const CacheKeyBase &other) const {
148 : // reflexive
149 0 : if (this == &other) {
150 0 : return TRUE;
151 : }
152 0 : if (!CacheKey<T>::operator == (other)) {
153 0 : 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 0 : static_cast<const LocaleCacheKey<T> *>(&other);
159 0 : return fLoc == fOther->fLoc;
160 : }
161 0 : virtual CacheKeyBase *clone() const {
162 0 : 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 0 : virtual char *writeDescription(char *buffer, int32_t bufLen) const {
170 0 : const char *s = fLoc.getName();
171 0 : uprv_strncpy(buffer, s, bufLen);
172 0 : buffer[bufLen - 1] = 0;
173 0 : 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 0 : void get(
203 : const CacheKey<T>& key,
204 : const T *&ptr,
205 : UErrorCode &status) const {
206 0 : get(key, NULL, ptr, status);
207 0 : }
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 0 : void get(
225 : const CacheKey<T>& key,
226 : const void *creationContext,
227 : const T *&ptr,
228 : UErrorCode &status) const {
229 0 : if (U_FAILURE(status)) {
230 0 : return;
231 : }
232 0 : UErrorCode creationStatus = U_ZERO_ERROR;
233 0 : const SharedObject *value = NULL;
234 0 : _get(key, value, creationContext, creationStatus);
235 0 : const T *tvalue = (const T *) value;
236 0 : if (U_SUCCESS(creationStatus)) {
237 0 : SharedObject::copyPtr(tvalue, ptr);
238 : }
239 0 : SharedObject::clearPtr(tvalue);
240 : // Take care not to overwrite a warning status passed in with
241 : // another warning or U_ZERO_ERROR.
242 0 : if (status == U_ZERO_ERROR || U_FAILURE(creationStatus)) {
243 0 : 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 0 : static void getByLocale(
269 : const Locale &loc, const T *&ptr, UErrorCode &status) {
270 0 : const UnifiedCache *cache = getInstance(status);
271 0 : if (U_FAILURE(status)) {
272 0 : return;
273 : }
274 0 : 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
|