| 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 |