OLD | NEW |
1 /* | 1 /* |
2 ****************************************************************************** | 2 ****************************************************************************** |
3 * Copyright (C) 2014, International Business Machines Corporation and | 3 * Copyright (C) 2015, International Business Machines Corporation and |
4 * others. All Rights Reserved. | 4 * others. All Rights Reserved. |
5 ****************************************************************************** | 5 ****************************************************************************** |
6 * | 6 * |
7 * File UNIFIEDCACHE.CPP | 7 * File UNIFIEDCACHE.CPP |
8 ****************************************************************************** | 8 ****************************************************************************** |
9 */ | 9 */ |
10 | 10 |
11 #include "uhash.h" | 11 #include "uhash.h" |
12 #include "unifiedcache.h" | 12 #include "unifiedcache.h" |
13 #include "umutex.h" | 13 #include "umutex.h" |
14 #include "mutex.h" | 14 #include "mutex.h" |
15 #include "uassert.h" | 15 #include "uassert.h" |
16 #include "ucln_cmn.h" | 16 #include "ucln_cmn.h" |
17 | 17 |
18 static icu::UnifiedCache *gCache = NULL; | 18 static icu::UnifiedCache *gCache = NULL; |
19 static icu::SharedObject *gNoValue = NULL; | 19 static icu::SharedObject *gNoValue = NULL; |
20 static UMutex gCacheMutex = U_MUTEX_INITIALIZER; | 20 static UMutex gCacheMutex = U_MUTEX_INITIALIZER; |
21 static UConditionVar gInProgressValueAddedCond = U_CONDITION_INITIALIZER; | 21 static UConditionVar gInProgressValueAddedCond = U_CONDITION_INITIALIZER; |
22 static icu::UInitOnce gCacheInitOnce = U_INITONCE_INITIALIZER; | 22 static icu::UInitOnce gCacheInitOnce = U_INITONCE_INITIALIZER; |
| 23 static const int32_t MAX_EVICT_ITERATIONS = 10; |
| 24 |
| 25 static int32_t DEFAULT_MAX_UNUSED = 1000; |
| 26 static int32_t DEFAULT_PERCENTAGE_OF_IN_USE = 100; |
| 27 |
23 | 28 |
24 U_CDECL_BEGIN | 29 U_CDECL_BEGIN |
25 static UBool U_CALLCONV unifiedcache_cleanup() { | 30 static UBool U_CALLCONV unifiedcache_cleanup() { |
26 gCacheInitOnce.reset(); | 31 gCacheInitOnce.reset(); |
27 if (gCache) { | 32 if (gCache) { |
28 delete gCache; | 33 delete gCache; |
29 gCache = NULL; | 34 gCache = NULL; |
30 } | 35 } |
31 if (gNoValue) { | 36 if (gNoValue) { |
32 delete gNoValue; | 37 delete gNoValue; |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
78 delete gNoValue; | 83 delete gNoValue; |
79 gCache = NULL; | 84 gCache = NULL; |
80 gNoValue = NULL; | 85 gNoValue = NULL; |
81 return; | 86 return; |
82 } | 87 } |
83 // We add a softref because we want hash elements with gNoValue to be | 88 // We add a softref because we want hash elements with gNoValue to be |
84 // elligible for purging but we don't ever want gNoValue to be deleted. | 89 // elligible for purging but we don't ever want gNoValue to be deleted. |
85 gNoValue->addSoftRef(); | 90 gNoValue->addSoftRef(); |
86 } | 91 } |
87 | 92 |
88 const UnifiedCache *UnifiedCache::getInstance(UErrorCode &status) { | 93 UnifiedCache *UnifiedCache::getInstance(UErrorCode &status) { |
89 umtx_initOnce(gCacheInitOnce, &cacheInit, status); | 94 umtx_initOnce(gCacheInitOnce, &cacheInit, status); |
90 if (U_FAILURE(status)) { | 95 if (U_FAILURE(status)) { |
91 return NULL; | 96 return NULL; |
92 } | 97 } |
93 U_ASSERT(gCache != NULL); | 98 U_ASSERT(gCache != NULL); |
94 return gCache; | 99 return gCache; |
95 } | 100 } |
96 | 101 |
97 UnifiedCache::UnifiedCache(UErrorCode &status) { | 102 UnifiedCache::UnifiedCache(UErrorCode &status) : |
| 103 fHashtable(NULL), |
| 104 fEvictPos(UHASH_FIRST), |
| 105 fItemsInUseCount(0), |
| 106 fMaxUnused(DEFAULT_MAX_UNUSED), |
| 107 fMaxPercentageOfInUse(DEFAULT_PERCENTAGE_OF_IN_USE), |
| 108 fAutoEvictedCount(0) { |
98 if (U_FAILURE(status)) { | 109 if (U_FAILURE(status)) { |
99 return; | 110 return; |
100 } | 111 } |
101 U_ASSERT(gNoValue != NULL); | 112 U_ASSERT(gNoValue != NULL); |
102 fHashtable = uhash_open( | 113 fHashtable = uhash_open( |
103 &ucache_hashKeys, | 114 &ucache_hashKeys, |
104 &ucache_compareKeys, | 115 &ucache_compareKeys, |
105 NULL, | 116 NULL, |
106 &status); | 117 &status); |
107 if (U_FAILURE(status)) { | 118 if (U_FAILURE(status)) { |
108 return; | 119 return; |
109 } | 120 } |
110 uhash_setKeyDeleter(fHashtable, &ucache_deleteKey); | 121 uhash_setKeyDeleter(fHashtable, &ucache_deleteKey); |
111 } | 122 } |
112 | 123 |
| 124 void UnifiedCache::setEvictionPolicy( |
| 125 int32_t count, int32_t percentageOfInUseItems, UErrorCode &status) { |
| 126 if (U_FAILURE(status)) { |
| 127 return; |
| 128 } |
| 129 if (count < 0 || percentageOfInUseItems < 0) { |
| 130 status = U_ILLEGAL_ARGUMENT_ERROR; |
| 131 return; |
| 132 } |
| 133 Mutex lock(&gCacheMutex); |
| 134 fMaxUnused = count; |
| 135 fMaxPercentageOfInUse = percentageOfInUseItems; |
| 136 } |
| 137 |
| 138 int32_t UnifiedCache::unusedCount() const { |
| 139 Mutex lock(&gCacheMutex); |
| 140 return uhash_count(fHashtable) - fItemsInUseCount; |
| 141 } |
| 142 |
| 143 int64_t UnifiedCache::autoEvictedCount() const { |
| 144 Mutex lock(&gCacheMutex); |
| 145 return fAutoEvictedCount; |
| 146 } |
| 147 |
113 int32_t UnifiedCache::keyCount() const { | 148 int32_t UnifiedCache::keyCount() const { |
114 Mutex lock(&gCacheMutex); | 149 Mutex lock(&gCacheMutex); |
115 return uhash_count(fHashtable); | 150 return uhash_count(fHashtable); |
116 } | 151 } |
117 | 152 |
118 void UnifiedCache::flush() const { | 153 void UnifiedCache::flush() const { |
119 Mutex lock(&gCacheMutex); | 154 Mutex lock(&gCacheMutex); |
120 | 155 |
121 // Use a loop in case cache items that are flushed held hard references to | 156 // Use a loop in case cache items that are flushed held hard references to |
122 // other cache items making those additional cache items eligible for | 157 // other cache items making those additional cache items eligible for |
123 // flushing. | 158 // flushing. |
124 while (_flush(FALSE)); | 159 while (_flush(FALSE)); |
125 umtx_condBroadcast(&gInProgressValueAddedCond); | |
126 } | 160 } |
127 | 161 |
128 #ifdef UNIFIED_CACHE_DEBUG | 162 #ifdef UNIFIED_CACHE_DEBUG |
129 #include <stdio.h> | 163 #include <stdio.h> |
130 | 164 |
131 void UnifiedCache::dump() { | 165 void UnifiedCache::dump() { |
132 UErrorCode status = U_ZERO_ERROR; | 166 UErrorCode status = U_ZERO_ERROR; |
133 const UnifiedCache *cache = getInstance(status); | 167 const UnifiedCache *cache = getInstance(status); |
134 if (U_FAILURE(status)) { | 168 if (U_FAILURE(status)) { |
135 fprintf(stderr, "Unified Cache: Error fetching cache.\n"); | 169 fprintf(stderr, "Unified Cache: Error fetching cache.\n"); |
136 return; | 170 return; |
137 } | 171 } |
138 cache->dumpContents(); | 172 cache->dumpContents(); |
139 } | 173 } |
140 | 174 |
141 void UnifiedCache::dumpContents() const { | 175 void UnifiedCache::dumpContents() const { |
142 Mutex lock(&gCacheMutex); | 176 Mutex lock(&gCacheMutex); |
143 _dumpContents(); | 177 _dumpContents(); |
144 } | 178 } |
145 | 179 |
146 // Dumps content of cache. | 180 // Dumps content of cache. |
147 // On entry, gCacheMutex must be held. | 181 // On entry, gCacheMutex must be held. |
148 // On exit, cache contents dumped to stderr. | 182 // On exit, cache contents dumped to stderr. |
149 void UnifiedCache::_dumpContents() const { | 183 void UnifiedCache::_dumpContents() const { |
150 int32_t pos = -1; | 184 int32_t pos = UHASH_FIRST; |
151 const UHashElement *element = uhash_nextElement(fHashtable, &pos); | 185 const UHashElement *element = uhash_nextElement(fHashtable, &pos); |
152 char buffer[256]; | 186 char buffer[256]; |
153 int32_t cnt = 0; | 187 int32_t cnt = 0; |
154 for (; element != NULL; element = uhash_nextElement(fHashtable, &pos)) { | 188 for (; element != NULL; element = uhash_nextElement(fHashtable, &pos)) { |
155 const SharedObject *sharedObject = | 189 const SharedObject *sharedObject = |
156 (const SharedObject *) element->value.pointer; | 190 (const SharedObject *) element->value.pointer; |
157 const CacheKeyBase *key = | 191 const CacheKeyBase *key = |
158 (const CacheKeyBase *) element->key.pointer; | 192 (const CacheKeyBase *) element->key.pointer; |
159 if (!sharedObject->allSoftReferences()) { | 193 if (sharedObject->hasHardReferences()) { |
160 ++cnt; | 194 ++cnt; |
161 fprintf( | 195 fprintf( |
162 stderr, | 196 stderr, |
163 "Unified Cache: Key '%s', error %d, value %p, total refcount
%d, soft refcount %d\n", | 197 "Unified Cache: Key '%s', error %d, value %p, total refcount
%d, soft refcount %d\n", |
164 key->writeDescription(buffer, 256), | 198 key->writeDescription(buffer, 256), |
165 key->creationStatus, | 199 key->creationStatus, |
166 sharedObject == gNoValue ? NULL :sharedObject, | 200 sharedObject == gNoValue ? NULL :sharedObject, |
167 sharedObject->getRefCount(), | 201 sharedObject->getRefCount(), |
168 sharedObject->getSoftRefCount()); | 202 sharedObject->getSoftRefCount()); |
169 } | 203 } |
170 } | 204 } |
171 fprintf(stderr, "Unified Cache: %d out of a total of %d still have hard refe
rences\n", cnt, uhash_count(fHashtable)); | 205 fprintf(stderr, "Unified Cache: %d out of a total of %d still have hard refe
rences\n", cnt, uhash_count(fHashtable)); |
172 } | 206 } |
173 #endif | 207 #endif |
174 | 208 |
175 UnifiedCache::~UnifiedCache() { | 209 UnifiedCache::~UnifiedCache() { |
176 // Try our best to clean up first. | 210 // Try our best to clean up first. |
177 flush(); | 211 flush(); |
178 { | 212 { |
179 // Now all that should be left in the cache are entries that refer to | 213 // Now all that should be left in the cache are entries that refer to |
180 // each other and entries with hard references from outside the cache. | 214 // each other and entries with hard references from outside the cache. |
181 // Nothing we can do about these so proceed to wipe out the cache. | 215 // Nothing we can do about these so proceed to wipe out the cache. |
182 Mutex lock(&gCacheMutex); | 216 Mutex lock(&gCacheMutex); |
183 _flush(TRUE); | 217 _flush(TRUE); |
184 } | 218 } |
185 uhash_close(fHashtable); | 219 uhash_close(fHashtable); |
186 } | 220 } |
187 | 221 |
| 222 // Returns the next element in the cache round robin style. |
| 223 // On entry, gCacheMutex must be held. |
| 224 const UHashElement * |
| 225 UnifiedCache::_nextElement() const { |
| 226 const UHashElement *element = uhash_nextElement(fHashtable, &fEvictPos); |
| 227 if (element == NULL) { |
| 228 fEvictPos = UHASH_FIRST; |
| 229 return uhash_nextElement(fHashtable, &fEvictPos); |
| 230 } |
| 231 return element; |
| 232 } |
| 233 |
188 // Flushes the contents of the cache. If cache values hold references to other | 234 // Flushes the contents of the cache. If cache values hold references to other |
189 // cache values then _flush should be called in a loop until it returns FALSE. | 235 // cache values then _flush should be called in a loop until it returns FALSE. |
190 // On entry, gCacheMutex must be held. | 236 // On entry, gCacheMutex must be held. |
191 // On exit, those values with only soft references are flushed. If all is true | 237 // On exit, those values with are evictable are flushed. If all is true |
192 // then every value is flushed even if hard references are held. | 238 // then every value is flushed even if it is not evictable. |
193 // Returns TRUE if any value in cache was flushed or FALSE otherwise. | 239 // Returns TRUE if any value in cache was flushed or FALSE otherwise. |
194 UBool UnifiedCache::_flush(UBool all) const { | 240 UBool UnifiedCache::_flush(UBool all) const { |
195 UBool result = FALSE; | 241 UBool result = FALSE; |
196 int32_t pos = -1; | 242 int32_t origSize = uhash_count(fHashtable); |
197 const UHashElement *element = uhash_nextElement(fHashtable, &pos); | 243 for (int32_t i = 0; i < origSize; ++i) { |
198 for (; element != NULL; element = uhash_nextElement(fHashtable, &pos)) { | 244 const UHashElement *element = _nextElement(); |
199 const SharedObject *sharedObject = | 245 if (all || _isEvictable(element)) { |
200 (const SharedObject *) element->value.pointer; | 246 const SharedObject *sharedObject = |
201 if (all || sharedObject->allSoftReferences()) { | 247 (const SharedObject *) element->value.pointer; |
202 uhash_removeElement(fHashtable, element); | 248 uhash_removeElement(fHashtable, element); |
203 sharedObject->removeSoftRef(); | 249 sharedObject->removeSoftRef(); |
204 result = TRUE; | 250 result = TRUE; |
205 } | 251 } |
206 } | 252 } |
207 return result; | 253 return result; |
208 } | 254 } |
209 | 255 |
| 256 // Computes how many items should be evicted. |
| 257 // On entry, gCacheMutex must be held. |
| 258 // Returns number of items that should be evicted or a value <= 0 if no |
| 259 // items need to be evicted. |
| 260 int32_t UnifiedCache::_computeCountOfItemsToEvict() const { |
| 261 int32_t maxPercentageOfInUseCount = |
| 262 fItemsInUseCount * fMaxPercentageOfInUse / 100; |
| 263 int32_t maxUnusedCount = fMaxUnused; |
| 264 if (maxUnusedCount < maxPercentageOfInUseCount) { |
| 265 maxUnusedCount = maxPercentageOfInUseCount; |
| 266 } |
| 267 return uhash_count(fHashtable) - fItemsInUseCount - maxUnusedCount; |
| 268 } |
| 269 |
| 270 // Run an eviction slice. |
| 271 // On entry, gCacheMutex must be held. |
| 272 // _runEvictionSlice runs a slice of the evict pipeline by examining the next |
| 273 // 10 entries in the cache round robin style evicting them if they are eligible. |
| 274 void UnifiedCache::_runEvictionSlice() const { |
| 275 int32_t maxItemsToEvict = _computeCountOfItemsToEvict(); |
| 276 if (maxItemsToEvict <= 0) { |
| 277 return; |
| 278 } |
| 279 for (int32_t i = 0; i < MAX_EVICT_ITERATIONS; ++i) { |
| 280 const UHashElement *element = _nextElement(); |
| 281 if (_isEvictable(element)) { |
| 282 const SharedObject *sharedObject = |
| 283 (const SharedObject *) element->value.pointer; |
| 284 uhash_removeElement(fHashtable, element); |
| 285 sharedObject->removeSoftRef(); |
| 286 ++fAutoEvictedCount; |
| 287 if (--maxItemsToEvict == 0) { |
| 288 break; |
| 289 } |
| 290 } |
| 291 } |
| 292 } |
| 293 |
| 294 |
210 // Places a new value and creationStatus in the cache for the given key. | 295 // Places a new value and creationStatus in the cache for the given key. |
211 // On entry, gCacheMutex must be held. key must not exist in the cache. | 296 // On entry, gCacheMutex must be held. key must not exist in the cache. |
212 // On exit, value and creation status placed under key. Soft reference added | 297 // On exit, value and creation status placed under key. Soft reference added |
213 // to value on successful add. On error sets status. | 298 // to value on successful add. On error sets status. |
214 void UnifiedCache::_putNew( | 299 void UnifiedCache::_putNew( |
215 const CacheKeyBase &key, | 300 const CacheKeyBase &key, |
216 const SharedObject *value, | 301 const SharedObject *value, |
217 const UErrorCode creationStatus, | 302 const UErrorCode creationStatus, |
218 UErrorCode &status) const { | 303 UErrorCode &status) const { |
219 if (U_FAILURE(status)) { | 304 if (U_FAILURE(status)) { |
220 return; | 305 return; |
221 } | 306 } |
222 CacheKeyBase *keyToAdopt = key.clone(); | 307 CacheKeyBase *keyToAdopt = key.clone(); |
223 if (keyToAdopt == NULL) { | 308 if (keyToAdopt == NULL) { |
224 status = U_MEMORY_ALLOCATION_ERROR; | 309 status = U_MEMORY_ALLOCATION_ERROR; |
225 return; | 310 return; |
226 } | 311 } |
227 keyToAdopt->creationStatus = creationStatus; | 312 keyToAdopt->fCreationStatus = creationStatus; |
| 313 if (value->noSoftReferences()) { |
| 314 _registerMaster(keyToAdopt, value); |
| 315 } |
228 uhash_put(fHashtable, keyToAdopt, (void *) value, &status); | 316 uhash_put(fHashtable, keyToAdopt, (void *) value, &status); |
229 if (U_SUCCESS(status)) { | 317 if (U_SUCCESS(status)) { |
230 value->addSoftRef(); | 318 value->addSoftRef(); |
231 } | 319 } |
232 } | 320 } |
233 | 321 |
234 // Places value and status at key if there is no value at key or if cache | 322 // Places value and status at key if there is no value at key or if cache |
235 // entry for key is in progress. Otherwise, it leaves the current value and | 323 // entry for key is in progress. Otherwise, it leaves the current value and |
236 // status there. | 324 // status there. |
237 // On entry. gCacheMutex must not be held. value must be | 325 // On entry. gCacheMutex must not be held. value must be |
238 // included in the reference count of the object to which it points. | 326 // included in the reference count of the object to which it points. |
239 // On exit, value and status are changed to what was already in the cache if | 327 // On exit, value and status are changed to what was already in the cache if |
240 // something was there and not in progress. Otherwise, value and status are left | 328 // something was there and not in progress. Otherwise, value and status are left |
241 // unchanged in which case they are placed in the cache on a best-effort basis. | 329 // unchanged in which case they are placed in the cache on a best-effort basis. |
242 // Caller must call removeRef() on value. | 330 // Caller must call removeRef() on value. |
243 void UnifiedCache::_putIfAbsentAndGet( | 331 void UnifiedCache::_putIfAbsentAndGet( |
244 const CacheKeyBase &key, | 332 const CacheKeyBase &key, |
245 const SharedObject *&value, | 333 const SharedObject *&value, |
246 UErrorCode &status) const { | 334 UErrorCode &status) const { |
247 Mutex lock(&gCacheMutex); | 335 Mutex lock(&gCacheMutex); |
248 const UHashElement *element = uhash_find(fHashtable, &key); | 336 const UHashElement *element = uhash_find(fHashtable, &key); |
249 if (element != NULL && !_inProgress(element)) { | 337 if (element != NULL && !_inProgress(element)) { |
250 _fetch(element, value, status); | 338 _fetch(element, value, status); |
251 return; | 339 return; |
252 } | 340 } |
253 if (element == NULL) { | 341 if (element == NULL) { |
254 UErrorCode putError = U_ZERO_ERROR; | 342 UErrorCode putError = U_ZERO_ERROR; |
255 // best-effort basis only. | 343 // best-effort basis only. |
256 _putNew(key, value, status, putError); | 344 _putNew(key, value, status, putError); |
257 return; | 345 } else { |
| 346 _put(element, value, status); |
258 } | 347 } |
259 _put(element, value, status); | 348 // Run an eviction slice. This will run even if we added a master entry |
| 349 // which doesn't increase the unused count, but that is still o.k |
| 350 _runEvictionSlice(); |
260 } | 351 } |
261 | 352 |
262 // Attempts to fetch value and status for key from cache. | 353 // Attempts to fetch value and status for key from cache. |
263 // On entry, gCacheMutex must not be held value must be NULL and status must | 354 // On entry, gCacheMutex must not be held value must be NULL and status must |
264 // be U_ZERO_ERROR. | 355 // be U_ZERO_ERROR. |
265 // On exit, either returns FALSE (In this | 356 // On exit, either returns FALSE (In this |
266 // case caller should try to create the object) or returns TRUE with value | 357 // case caller should try to create the object) or returns TRUE with value |
267 // pointing to the fetched value and status set to fetched status. When | 358 // pointing to the fetched value and status set to fetched status. When |
268 // FALSE is returned status may be set to failure if an in progress hash | 359 // FALSE is returned status may be set to failure if an in progress hash |
269 // entry could not be made but value will remain unchanged. When TRUE is | 360 // entry could not be made but value will remain unchanged. When TRUE is |
(...skipping 17 matching lines...) Expand all Loading... |
287 _putNew(key, gNoValue, U_ZERO_ERROR, status); | 378 _putNew(key, gNoValue, U_ZERO_ERROR, status); |
288 return FALSE; | 379 return FALSE; |
289 } | 380 } |
290 | 381 |
291 // Gets value out of cache. | 382 // Gets value out of cache. |
292 // On entry. gCacheMutex must not be held. value must be NULL. status | 383 // On entry. gCacheMutex must not be held. value must be NULL. status |
293 // must be U_ZERO_ERROR. | 384 // must be U_ZERO_ERROR. |
294 // On exit. value and status set to what is in cache at key or on cache | 385 // On exit. value and status set to what is in cache at key or on cache |
295 // miss the key's createObject() is called and value and status are set to | 386 // miss the key's createObject() is called and value and status are set to |
296 // the result of that. In this latter case, best effort is made to add the | 387 // the result of that. In this latter case, best effort is made to add the |
297 // value and status to the cache. value will be set to NULL instead of | 388 // value and status to the cache. If createObject() fails to create a value, |
298 // gNoValue. Caller must call removeRef on value if non NULL. | 389 // gNoValue is stored in cache, and value is set to NULL. Caller must call |
| 390 // removeRef on value if non NULL. |
299 void UnifiedCache::_get( | 391 void UnifiedCache::_get( |
300 const CacheKeyBase &key, | 392 const CacheKeyBase &key, |
301 const SharedObject *&value, | 393 const SharedObject *&value, |
302 const void *creationContext, | 394 const void *creationContext, |
303 UErrorCode &status) const { | 395 UErrorCode &status) const { |
304 U_ASSERT(value == NULL); | 396 U_ASSERT(value == NULL); |
305 U_ASSERT(status == U_ZERO_ERROR); | 397 U_ASSERT(status == U_ZERO_ERROR); |
306 if (_poll(key, value, status)) { | 398 if (_poll(key, value, status)) { |
307 if (value == gNoValue) { | 399 if (value == gNoValue) { |
308 SharedObject::clearPtr(value); | 400 SharedObject::clearPtr(value); |
309 } | 401 } |
310 return; | 402 return; |
311 } | 403 } |
312 if (U_FAILURE(status)) { | 404 if (U_FAILURE(status)) { |
313 return; | 405 return; |
314 } | 406 } |
315 value = key.createObject(creationContext, status); | 407 value = key.createObject(creationContext, status); |
316 U_ASSERT(value == NULL || !value->allSoftReferences()); | 408 U_ASSERT(value == NULL || value->hasHardReferences()); |
317 U_ASSERT(value != NULL || status != U_ZERO_ERROR); | 409 U_ASSERT(value != NULL || status != U_ZERO_ERROR); |
318 if (value == NULL) { | 410 if (value == NULL) { |
319 SharedObject::copyPtr(gNoValue, value); | 411 SharedObject::copyPtr(gNoValue, value); |
320 } | 412 } |
321 _putIfAbsentAndGet(key, value, status); | 413 _putIfAbsentAndGet(key, value, status); |
322 if (value == gNoValue) { | 414 if (value == gNoValue) { |
323 SharedObject::clearPtr(value); | 415 SharedObject::clearPtr(value); |
324 } | 416 } |
325 } | 417 } |
326 | 418 |
| 419 void UnifiedCache::decrementItemsInUseWithLockingAndEviction() const { |
| 420 Mutex mutex(&gCacheMutex); |
| 421 decrementItemsInUse(); |
| 422 _runEvictionSlice(); |
| 423 } |
| 424 |
| 425 void UnifiedCache::incrementItemsInUse() const { |
| 426 ++fItemsInUseCount; |
| 427 } |
| 428 |
| 429 void UnifiedCache::decrementItemsInUse() const { |
| 430 --fItemsInUseCount; |
| 431 } |
| 432 |
| 433 // Register a master cache entry. |
| 434 // On entry, gCacheMutex must be held. |
| 435 // On exit, items in use count incremented, entry is marked as a master |
| 436 // entry, and value registered with cache so that subsequent calls to |
| 437 // addRef() and removeRef() on it correctly updates items in use count |
| 438 void UnifiedCache::_registerMaster( |
| 439 const CacheKeyBase *theKey, const SharedObject *value) const { |
| 440 theKey->fIsMaster = TRUE; |
| 441 ++fItemsInUseCount; |
| 442 value->registerWithCache(this); |
| 443 } |
| 444 |
327 // Store a value and error in given hash entry. | 445 // Store a value and error in given hash entry. |
328 // On entry, gCacheMutex must be held. Hash entry element must be in progress. | 446 // On entry, gCacheMutex must be held. Hash entry element must be in progress. |
329 // value must be non NULL. | 447 // value must be non NULL. |
330 // On Exit, soft reference added to value. value and status stored in hash | 448 // On Exit, soft reference added to value. value and status stored in hash |
331 // entry. Soft reference removed from previous stored value. Waiting | 449 // entry. Soft reference removed from previous stored value. Waiting |
332 // threads notified. | 450 // threads notified. |
333 void UnifiedCache::_put( | 451 void UnifiedCache::_put( |
334 const UHashElement *element, | 452 const UHashElement *element, |
335 const SharedObject *value, | 453 const SharedObject *value, |
336 const UErrorCode status) { | 454 const UErrorCode status) const { |
337 U_ASSERT(_inProgress(element)); | 455 U_ASSERT(_inProgress(element)); |
338 const CacheKeyBase *theKey = (const CacheKeyBase *) element->key.pointer; | 456 const CacheKeyBase *theKey = (const CacheKeyBase *) element->key.pointer; |
339 const SharedObject *oldValue = (const SharedObject *) element->value.pointer
; | 457 const SharedObject *oldValue = (const SharedObject *) element->value.pointer
; |
340 theKey->creationStatus = status; | 458 theKey->fCreationStatus = status; |
| 459 if (value->noSoftReferences()) { |
| 460 _registerMaster(theKey, value); |
| 461 } |
341 value->addSoftRef(); | 462 value->addSoftRef(); |
342 UHashElement *ptr = const_cast<UHashElement *>(element); | 463 UHashElement *ptr = const_cast<UHashElement *>(element); |
343 ptr->value.pointer = (void *) value; | 464 ptr->value.pointer = (void *) value; |
344 oldValue->removeSoftRef(); | 465 oldValue->removeSoftRef(); |
345 | 466 |
346 // Tell waiting threads that we replace in-progress status with | 467 // Tell waiting threads that we replace in-progress status with |
347 // an error. | 468 // an error. |
348 umtx_condBroadcast(&gInProgressValueAddedCond); | 469 umtx_condBroadcast(&gInProgressValueAddedCond); |
349 } | 470 } |
350 | 471 |
| 472 void |
| 473 UnifiedCache::copyPtr(const SharedObject *src, const SharedObject *&dest) { |
| 474 if(src != dest) { |
| 475 if(dest != NULL) { |
| 476 dest->removeRefWhileHoldingCacheLock(); |
| 477 } |
| 478 dest = src; |
| 479 if(src != NULL) { |
| 480 src->addRefWhileHoldingCacheLock(); |
| 481 } |
| 482 } |
| 483 } |
| 484 |
| 485 void |
| 486 UnifiedCache::clearPtr(const SharedObject *&ptr) { |
| 487 if (ptr != NULL) { |
| 488 ptr->removeRefWhileHoldingCacheLock(); |
| 489 ptr = NULL; |
| 490 } |
| 491 } |
| 492 |
| 493 |
351 // Fetch value and error code from a particular hash entry. | 494 // Fetch value and error code from a particular hash entry. |
352 // On entry, gCacheMutex must be held. value must be either NULL or must be | 495 // On entry, gCacheMutex must be held. value must be either NULL or must be |
353 // included in the ref count of the object to which it points. | 496 // included in the ref count of the object to which it points. |
354 // On exit, value and status set to what is in the hash entry. Caller must | 497 // On exit, value and status set to what is in the hash entry. Caller must |
355 // eventually call removeRef on value. | 498 // eventually call removeRef on value. |
356 // If hash entry is in progress, value will be set to gNoValue and status will | 499 // If hash entry is in progress, value will be set to gNoValue and status will |
357 // be set to U_ZERO_ERROR. | 500 // be set to U_ZERO_ERROR. |
358 void UnifiedCache::_fetch( | 501 void UnifiedCache::_fetch( |
359 const UHashElement *element, | 502 const UHashElement *element, |
360 const SharedObject *&value, | 503 const SharedObject *&value, |
361 UErrorCode &status) { | 504 UErrorCode &status) { |
362 const CacheKeyBase *theKey = (const CacheKeyBase *) element->key.pointer; | 505 const CacheKeyBase *theKey = (const CacheKeyBase *) element->key.pointer; |
363 status = theKey->creationStatus; | 506 status = theKey->fCreationStatus; |
364 SharedObject::copyPtr( | 507 |
365 (const SharedObject *) element->value.pointer, value); | 508 // Since we have the cache lock, calling regular SharedObject methods |
| 509 // could cause us to deadlock on ourselves since they may need to lock |
| 510 // the cache mutex. |
| 511 UnifiedCache::copyPtr((const SharedObject *) element->value.pointer, value); |
366 } | 512 } |
367 | 513 |
368 // Determine if given hash entry is in progress. | 514 // Determine if given hash entry is in progress. |
369 // On entry, gCacheMutex must be held. | 515 // On entry, gCacheMutex must be held. |
370 UBool UnifiedCache::_inProgress(const UHashElement *element) { | 516 UBool UnifiedCache::_inProgress(const UHashElement *element) { |
371 const SharedObject *value = NULL; | 517 const SharedObject *value = NULL; |
372 UErrorCode status = U_ZERO_ERROR; | 518 UErrorCode status = U_ZERO_ERROR; |
373 _fetch(element, value, status); | 519 _fetch(element, value, status); |
374 UBool result = (value == gNoValue && status == U_ZERO_ERROR); | 520 UBool result = _inProgress(value, status); |
375 SharedObject::clearPtr(value); | 521 |
| 522 // Since we have the cache lock, calling regular SharedObject methods |
| 523 // could cause us to deadlock on ourselves since they may need to lock |
| 524 // the cache mutex. |
| 525 UnifiedCache::clearPtr(value); |
376 return result; | 526 return result; |
377 } | 527 } |
378 | 528 |
| 529 // Determine if given hash entry is in progress. |
| 530 // On entry, gCacheMutex must be held. |
| 531 UBool UnifiedCache::_inProgress( |
| 532 const SharedObject *theValue, UErrorCode creationStatus) { |
| 533 return (theValue == gNoValue && creationStatus == U_ZERO_ERROR); |
| 534 } |
| 535 |
| 536 // Determine if given hash entry is eligible for eviction. |
| 537 // On entry, gCacheMutex must be held. |
| 538 UBool UnifiedCache::_isEvictable(const UHashElement *element) { |
| 539 const CacheKeyBase *theKey = (const CacheKeyBase *) element->key.pointer; |
| 540 const SharedObject *theValue = |
| 541 (const SharedObject *) element->value.pointer; |
| 542 |
| 543 // Entries that are under construction are never evictable |
| 544 if (_inProgress(theValue, theKey->fCreationStatus)) { |
| 545 return FALSE; |
| 546 } |
| 547 |
| 548 // We can evict entries that are either not a master or have just |
| 549 // one reference (The one reference being from the cache itself). |
| 550 return (!theKey->fIsMaster || (theValue->getSoftRefCount() == 1 && theValue-
>noHardReferences())); |
| 551 } |
| 552 |
379 U_NAMESPACE_END | 553 U_NAMESPACE_END |
OLD | NEW |