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; }
UBool UnifiedCache::_inProgress(const UHashElement* element) const { UErrorCode status = U_ZERO_ERROR; const SharedObject * value = NULL; _fetch(element, value, status); UBool result = _inProgress(value, status); removeHardRef(value); return result; }
// Determine if given hash entry is in progress. // On entry, gCacheMutex must be held. UBool UnifiedCache::_inProgress(const UHashElement *element) { const SharedObject *value = NULL; UErrorCode status = U_ZERO_ERROR; _fetch(element, value, status); UBool result = _inProgress(value, status); // Since we have the cache lock, calling regular SharedObject methods // could cause us to deadlock on ourselves since they may need to lock // the cache mutex. UnifiedCache::clearPtr(value); return result; }
// Determine if given hash entry is eligible for eviction. // On entry, gCacheMutex must be held. UBool UnifiedCache::_isEvictable(const UHashElement *element) { const CacheKeyBase *theKey = (const CacheKeyBase *) element->key.pointer; const SharedObject *theValue = (const SharedObject *) element->value.pointer; // Entries that are under construction are never evictable if (_inProgress(theValue, theKey->fCreationStatus)) { return FALSE; } // We can evict entries that are either not a master or have just // one reference (The one reference being from the cache itself). return (!theKey->fIsMaster || (theValue->getSoftRefCount() == 1 && theValue->noHardReferences())); }
// Store a value and error in given hash entry. // On entry, gCacheMutex must be held. Hash entry element must be in progress. // value must be non NULL. // On Exit, soft reference added to value. value and status stored in hash // entry. Soft reference removed from previous stored value. Waiting // threads notified. void UnifiedCache::_put( const UHashElement *element, const SharedObject *value, const UErrorCode status) { U_ASSERT(_inProgress(element)); const CacheKeyBase *theKey = (const CacheKeyBase *) element->key.pointer; const SharedObject *oldValue = (const SharedObject *) element->value.pointer; theKey->creationStatus = status; value->addSoftRef(); UHashElement *ptr = const_cast<UHashElement *>(element); ptr->value.pointer = (void *) value; oldValue->removeSoftRef(); // Tell waiting threads that we replace in-progress status with // an error. umtx_condBroadcast(&gInProgressValueAddedCond); }
// 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); }
// 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); } 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(); }
void UnifiedCache::_put( const UHashElement *element, const SharedObject *value, const UErrorCode status) const { U_ASSERT(_inProgress(element)); const CacheKeyBase *theKey = (const CacheKeyBase *) element->key.pointer; const SharedObject *oldValue = (const SharedObject *) element->value.pointer; theKey->fCreationStatus = status; if (value->softRefCount == 0) { _registerMaster(theKey, value); } value->softRefCount++; UHashElement *ptr = const_cast<UHashElement *>(element); ptr->value.pointer = (void *) value; U_ASSERT(oldValue == fNoValue); removeSoftRef(oldValue); // Tell waiting threads that we replace in-progress status with // an error. umtx_condBroadcast(gInProgressValueAddedCond()); }