UBool UnifiedCache::_poll( const CacheKeyBase &key, const SharedObject *&value, UErrorCode &status) const { U_ASSERT(value == NULL); U_ASSERT(status == U_ZERO_ERROR); Mutex lock(gCacheMutex()); const UHashElement *element = uhash_find(fHashtable, &key); // If the hash table contains an inProgress placeholder entry for this key, // this means that another thread is currently constructing the value object. // Loop, waiting for that construction to complete. while (element != NULL && _inProgress(element)) { umtx_condWait(gInProgressValueAddedCond(), gCacheMutex()); element = uhash_find(fHashtable, &key); } // If the hash table contains an entry for the key, // fetch out the contents and return them. if (element != NULL) { _fetch(element, value, status); return TRUE; } // The hash table contained nothing for this key. // Insert an inProgress place holder value. // Our caller will create the final value and update the hash table. _putNew(key, fNoValue, U_ZERO_ERROR, status); return FALSE; }
StringTrieBuilder::Node * StringTrieBuilder::registerFinalValue(int32_t value, UErrorCode &errorCode) { if(U_FAILURE(errorCode)) { return NULL; } FinalValueNode key(value); const UHashElement *old=uhash_find(nodes, &key); if(old!=NULL) { return (Node *)old->key.pointer; } Node *newNode=new FinalValueNode(value); if(newNode==NULL) { errorCode=U_MEMORY_ALLOCATION_ERROR; return NULL; } // If uhash_puti() returns a non-zero value from an equivalent, previously // registered node, then uhash_find() failed to find that and we will leak newNode. #if !U_RELEASE int32_t oldValue= // Only in debug mode to avoid a compiler warning about unused oldValue. #endif uhash_puti(nodes, newNode, 1, &errorCode); U_ASSERT(oldValue==0); if(U_FAILURE(errorCode)) { delete newNode; return NULL; } return newNode; }
// Attempts to fetch value and status for key from cache. // On entry, gCacheMutex must not be held value must be NULL and status must // be U_ZERO_ERROR. // On exit, either returns FALSE (In this // case caller should try to create the object) or returns TRUE with value // pointing to the fetched value and status set to fetched status. When // FALSE is returned status may be set to failure if an in progress hash // entry could not be made but value will remain unchanged. When TRUE is // returned, caler must call removeRef() on value. UBool UnifiedCache::_poll( const CacheKeyBase &key, const SharedObject *&value, UErrorCode &status) const { U_ASSERT(value == NULL); U_ASSERT(status == U_ZERO_ERROR); Mutex lock(&gCacheMutex); const UHashElement *element = uhash_find(fHashtable, &key); while (element != NULL && _inProgress(element)) { umtx_condWait(&gInProgressValueAddedCond, &gCacheMutex); element = uhash_find(fHashtable, &key); } if (element != NULL) { _fetch(element, value, status); return TRUE; } _putNew(key, gNoValue, U_ZERO_ERROR, status); return FALSE; }
// Places value and status at key if there is no value at key or if cache // entry for key is in progress. Otherwise, it leaves the current value and // status there. // On entry. gCacheMutex must not be held. value must be // included in the reference count of the object to which it points. // On exit, value and status are changed to what was already in the cache if // something was there and not in progress. Otherwise, value and status are left // unchanged in which case they are placed in the cache on a best-effort basis. // Caller must call removeRef() on value. void UnifiedCache::_putIfAbsentAndGet( const CacheKeyBase &key, const SharedObject *&value, UErrorCode &status) const { Mutex lock(&gCacheMutex); const UHashElement *element = uhash_find(fHashtable, &key); if (element != NULL && !_inProgress(element)) { _fetch(element, value, status); return; } if (element == NULL) { UErrorCode putError = U_ZERO_ERROR; // best-effort basis only. _putNew(key, value, status, putError); return; } _put(element, value, status); }
// Places value and status at key if there is no value at key or if cache // entry for key is in progress. Otherwise, it leaves the current value and // status there. // On entry. gCacheMutex must not be held. value must be // included in the reference count of the object to which it points. // On exit, value and status are changed to what was already in the cache if // something was there and not in progress. Otherwise, value and status are left // unchanged in which case they are placed in the cache on a best-effort basis. // Caller must call removeRef() on value. void UnifiedCache::_putIfAbsentAndGet( const CacheKeyBase &key, const SharedObject *&value, UErrorCode &status) const { Mutex lock(&gCacheMutex); const UHashElement *element = uhash_find(fHashtable, &key); if (element != NULL && !_inProgress(element)) { _fetch(element, value, status); return; } if (element == NULL) { UErrorCode putError = U_ZERO_ERROR; // best-effort basis only. _putNew(key, value, status, putError); } else { _put(element, value, status); } // Run an eviction slice. This will run even if we added a master entry // which doesn't increase the unused count, but that is still o.k _runEvictionSlice(); }
static void TestOtherAPI(void){ UErrorCode status = U_ZERO_ERROR; UHashtable *hash; /* Use the correct type when cast to void * */ static const UChar one[4] = {0x006F, 0x006E, 0x0065, 0}; /* L"one" */ static const UChar one2[4] = {0x006F, 0x006E, 0x0065, 0}; /* Get around compiler optimizations */ static const UChar two[4] = {0x0074, 0x0077, 0x006F, 0}; /* L"two" */ static const UChar two2[4] = {0x0074, 0x0077, 0x006F, 0}; /* L"two" */ static const UChar three[6] = {0x0074, 0x0068, 0x0072, 0x0065, 0x0065, 0}; /* L"three" */ static const UChar four[6] = {0x0066, 0x006F, 0x0075, 0x0072, 0}; /* L"four" */ static const UChar five[6] = {0x0066, 0x0069, 0x0076, 0x0065, 0}; /* L"five" */ static const UChar five2[6] = {0x0066, 0x0069, 0x0076, 0x0065, 0}; /* L"five" */ hash = uhash_open(uhash_hashUChars, uhash_compareUChars, NULL, &status); if (U_FAILURE(status)) { log_err("FAIL: uhash_open failed with %s and returned 0x%08x\n", u_errorName(status), hash); return; } if (hash == NULL) { log_err("FAIL: uhash_open returned NULL\n"); return; } log_verbose("Ok: uhash_open returned 0x%08X\n", hash); uhash_puti(hash, (void*)one, 1, &status); if(uhash_count(hash) != 1){ log_err("FAIL: uhas_count() failed. Expected: 1, Got: %d\n", uhash_count(hash)); } if(uhash_find(hash, (void*)two) != NULL){ log_err("FAIL: uhash_find failed\n"); } uhash_puti(hash, (void*)two, 2, &status); uhash_puti(hash, (void*)three, 3, &status); uhash_puti(hash, (void*)four, 4, &status); uhash_puti(hash, (void*)five, 5, &status); if(uhash_count(hash) != 5){ log_err("FAIL: uhas_count() failed. Expected: 5, Got: %d\n", uhash_count(hash)); } if(uhash_geti(hash, (void*)two2) != 2){ log_err("FAIL: uhash_geti failed\n"); } if(uhash_find(hash, (void*)two2) == NULL){ log_err("FAIL: uhash_find of \"two\" failed\n"); } if(uhash_removei(hash, (void*)five2) != 5){ log_err("FAIL: uhash_remove() failed\n"); } if(uhash_count(hash) != 4){ log_err("FAIL: uhas_count() failed. Expected: 4, Got: %d\n", uhash_count(hash)); } uhash_put(hash, (void*)one, NULL, &status); if(uhash_count(hash) != 3){ log_err("FAIL: uhash_put() with value=NULL didn't remove the key value pair\n"); } status=U_ILLEGAL_ARGUMENT_ERROR; uhash_puti(hash, (void*)one, 1, &status); if(uhash_count(hash) != 3){ log_err("FAIL: uhash_put() with value!=NULL should fail when status != U_ZERO_ERROR \n"); } status=U_ZERO_ERROR; uhash_puti(hash, (void*)one, 1, &status); if(uhash_count(hash) != 4){ log_err("FAIL: uhash_put() with value!=NULL didn't replace the key value pair\n"); } if(_compareUChars((void*)one, (void*)two) == TRUE || _compareUChars((void*)one, (void*)one) != TRUE || _compareUChars((void*)one, (void*)one2) != TRUE || _compareUChars((void*)one, NULL) == TRUE ) { log_err("FAIL: compareUChars failed\n"); } uhash_removeAll(hash); if(uhash_count(hash) != 0){ log_err("FAIL: uhas_count() failed. Expected: 0, Got: %d\n", uhash_count(hash)); } uhash_setKeyComparator(hash, uhash_compareLong); uhash_setKeyHasher(hash, uhash_hashLong); uhash_iputi(hash, 1001, 1, &status); uhash_iputi(hash, 1002, 2, &status); uhash_iputi(hash, 1003, 3, &status); if(_compareLong(1001, 1002) == TRUE || _compareLong(1001, 1001) != TRUE || _compareLong(1001, 0) == TRUE ) { log_err("FAIL: compareLong failed\n"); } /*set the resize policy to just GROW and SHRINK*/ /*how to test this??*/ uhash_setResizePolicy(hash, U_GROW_AND_SHRINK); uhash_iputi(hash, 1004, 4, &status); uhash_iputi(hash, 1005, 5, &status); uhash_iputi(hash, 1006, 6, &status); if(uhash_count(hash) != 6){ log_err("FAIL: uhash_count() failed. Expected: 6, Got: %d\n", uhash_count(hash)); } if(uhash_iremovei(hash, 1004) != 4){ log_err("FAIL: uhash_remove failed\n"); } if(uhash_iremovei(hash, 1004) != 0){ log_err("FAIL: uhash_remove failed\n"); } uhash_removeAll(hash); uhash_iput(hash, 2004, (void*)one, &status); uhash_iput(hash, 2005, (void*)two, &status); if(uhash_count(hash) != 2){ log_err("FAIL: uhash_count() failed. Expected: 2, Got: %d\n", uhash_count(hash)); } if(uhash_iremove(hash, 2004) != (void*)one){ log_err("FAIL: uhash_remove failed\n"); } if(uhash_iremove(hash, 2004) != NULL){ log_err("FAIL: uhash_remove failed\n"); } if(uhash_count(hash) != 1){ log_err("FAIL: uhash_count() failed. Expected: 1, Got: %d\n", uhash_count(hash)); } uhash_close(hash); }