// Record the actual id for this service in the cache, so we can return it // even if we succeed later with a different id. class CacheEntry : public UMemory { private:
int32_t refcount;
/** * Instantiation creates an initial reference, so don't call this * unless you're creating a new pointer to this. Management of * that pointer will have to know how to deal with refcounts. * Return true if the resource has not already been released.
*/
CacheEntry* ref() {
++refcount; returnthis;
}
/** * Destructions removes a reference, so don't call this unless * you're removing pointer to this somewhere. Management of that * pointer will have to know how to deal with refcounts. Once * the refcount drops to zero, the resource is released. Return * false if the resource has been released.
*/
CacheEntry* unref() { if ((--refcount) == 0) { deletethis; return nullptr;
} returnthis;
}
/** * Return true if there is at least one reference to this and the * resource has not been released.
*/
UBool isShared() const { return refcount > 1;
}
};
// this is a vector that subclasses of ICUService can override to further customize the result object // before returning it. All other public get functions should call this one.
// make it possible to call reentrantly on systems that don't have reentrant mutexes. // we can use this simple approach since we know the situation where we're calling // reentrantly even without knowing the thread. class XMutex : public UMemory { public: inline XMutex(UMutex *mutex, UBool reentering)
: fMutex(mutex)
, fActive(!reentering)
{ if (fActive) umtx_lock(fMutex);
} inline ~XMutex() { if (fActive) umtx_unlock(fMutex);
}
private:
UMutex *fMutex;
UBool fActive;
};
// called only by factories, treat as private
UObject*
ICUService::getKey(ICUServiceKey& key, UnicodeString* actualReturn, const ICUServiceFactory* factory, UErrorCode& status) const
{ if (U_FAILURE(status)) { return nullptr;
}
if (isDefault()) { return handleDefault(key, actualReturn, status);
}
CacheEntry* result = nullptr;
{ // The factory list can't be modified until we're done, // otherwise we might update the cache with an invalid result. // The cache has to stay in synch with the factory list. // ICU doesn't have monitors so we can't use rw locks, so // we single-thread everything using this service, for now.
// if factory is not null, we're calling from within the mutex, // and since some unix machines don't have reentrant mutexes we // need to make sure not to try to lock it again.
XMutex mutex(&lock, factory != nullptr);
if (serviceCache == nullptr) {
ncthis->serviceCache = new Hashtable(status); if (ncthis->serviceCache == nullptr) {
status = U_MEMORY_ALLOCATION_ERROR; return nullptr;
} if (U_FAILURE(status)) { delete serviceCache; return nullptr;
}
serviceCache->setValueDeleter(cacheDeleter);
}
if (factory != nullptr) { for (int32_t i = 0; i < limit; ++i) { if (factory == static_cast<const ICUServiceFactory*>(factories->elementAt(i))) {
startIndex = i + 1; break;
}
} if (startIndex == 0) { // throw new InternalError("Factory " + factory + "not registered with service: " + this);
status = U_ILLEGAL_ARGUMENT_ERROR; return nullptr;
}
cacheResult = false;
}
do {
currentDescriptor.remove();
key.currentDescriptor(currentDescriptor);
result = static_cast<CacheEntry*>(serviceCache->get(currentDescriptor)); if (result != nullptr) { break;
}
// first test of cache failed, so we'll have to update // the cache if we eventually succeed-- that is, if we're // going to update the cache at all.
putInCache = true;
int32_t index = startIndex; while (index < limit) {
ICUServiceFactory* f = static_cast<ICUServiceFactory*>(factories->elementAt(index++));
LocalPointer<UObject> service(f->create(key, this, status)); if (U_FAILURE(status)) { return nullptr;
} if (service.isValid()) {
result = new CacheEntry(currentDescriptor, service.getAlias()); if (result == nullptr) {
status = U_MEMORY_ALLOCATION_ERROR; return nullptr;
}
service.orphan(); // result now owns service.
goto outerEnd;
}
}
// prepare to load the cache with all additional ids that // will resolve to result, assuming we'll succeed. We // don't want to keep querying on an id that's going to // fallback to the one that succeeded, we want to hit the // cache the first time next goaround. if (cacheDescriptorList.isNull()) {
cacheDescriptorList.adoptInsteadAndCheckErrorCode(new UVector(uprv_deleteUObject, nullptr, 5, status), status); if (U_FAILURE(status)) { return nullptr;
}
}
LocalPointer<UnicodeString> idToCache(new UnicodeString(currentDescriptor), status); if (U_FAILURE(status)) { return nullptr;
} if (idToCache->isBogus()) {
status = U_MEMORY_ALLOCATION_ERROR; return nullptr;
}
cacheDescriptorList->adoptElement(idToCache.orphan(), status); if (U_FAILURE(status)) { return nullptr;
}
} while (key.fallback());
outerEnd:
if (result != nullptr) { if (putInCache && cacheResult) {
serviceCache->put(result->actualDescriptor, result, status); if (U_FAILURE(status)) { return nullptr;
}
if (cacheDescriptorList.isValid()) { for (int32_t i = cacheDescriptorList->size(); --i >= 0;) {
UnicodeString* desc = static_cast<UnicodeString*>(cacheDescriptorList->elementAt(i));
serviceCache->put(*desc, result, status); if (U_FAILURE(status)) { return nullptr;
}
if (dnCache == nullptr) { const Hashtable* m = getVisibleIDMap(status); if (U_FAILURE(status)) { return result;
}
ncthis->dnCache = new DNCache(locale); if (dnCache == nullptr) {
status = U_MEMORY_ALLOCATION_ERROR; return result;
}
int32_t pos = UHASH_FIRST; const UHashElement* entry = nullptr; while ((entry = m->nextElement(pos)) != nullptr) { const UnicodeString* id = static_cast<const UnicodeString*>(entry->key.pointer);
ICUServiceFactory* f = static_cast<ICUServiceFactory*>(entry->value.pointer);
UnicodeString dname;
f->getDisplayName(*id, locale, dname); if (dname.isBogus()) {
status = U_MEMORY_ALLOCATION_ERROR;
} else {
dnCache->cache.put(dname, (void*)id, status); // share pointer with visibleIDMap if (U_SUCCESS(status)) { continue;
}
} delete dnCache;
ncthis->dnCache = nullptr; return result;
}
}
}
ICUServiceKey* matchKey = createKey(matchID, status); /* To ensure that all elements in the hashtable are iterated, set pos to -1. * nextElement(pos) will skip the position at pos and begin the iteration * at the next position, which in this case will be 0.
*/
int32_t pos = UHASH_FIRST; const UHashElement *entry = nullptr; while ((entry = dnCache->cache.nextElement(pos)) != nullptr) { const UnicodeString* id = static_cast<const UnicodeString*>(entry->value.pointer); if (matchKey != nullptr && !matchKey->isFallbackOf(*id)) { continue;
} const UnicodeString* dn = static_cast<const UnicodeString*>(entry->key.pointer);
StringPair* sp = StringPair::create(*id, *dn, status);
result.adoptElement(sp, status); if (U_FAILURE(status)) {
result.removeAllElements(); break;
}
} delete matchKey;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.