icu4c/source/allinone/icucheck.bat -text
icu4c/source/common/common.vcxproj -text
icu4c/source/common/common.vcxproj.filters -text
+icu4c/source/common/unifiedcache.cpp -text
+icu4c/source/common/unifiedcache.h -text
icu4c/source/data/curr/pool.res -text
icu4c/source/data/in/coll/ucadata-implicithan.icu -text
icu4c/source/data/in/coll/ucadata-unihan.icu -text
icu4c/source/test/intltest/intltest.vcxproj -text
icu4c/source/test/intltest/intltest.vcxproj.filters -text
icu4c/source/test/intltest/numfmtspectest.cpp -text
+icu4c/source/test/intltest/unifiedcachetest.cpp -text
icu4c/source/test/iotest/iotest.vcxproj -text
icu4c/source/test/iotest/iotest.vcxproj.filters -text
icu4c/source/test/letest/cletest.vcxproj -text
uidna.o usprep.o uts46.o punycode.o \
util.o util_props.o parsepos.o locbased.o cwchar.o wintz.o dtintrv.o ucnvsel.o propsvec.o \
ulist.o uloc_tag.o icudataver.o icuplug.o listformatter.o lrucache.o \
-sharedobject.o simplepatternformatter.o
+sharedobject.o simplepatternformatter.o unifiedcache.o
## Header files to install
HEADERS = $(srcdir)/unicode/*.h
</ClCompile>\r
<ClCompile Include="uchar.c" />\r
<ClCompile Include="unames.cpp" />\r
+ <ClCompile Include="unifiedcache.cpp">\r
+ <DisableLanguageExtensions>false</DisableLanguageExtensions>\r
+ </ClCompile>\r
<ClCompile Include="unifilt.cpp" />\r
<ClCompile Include="unifunct.cpp" />\r
<ClCompile Include="uniset.cpp" />\r
</Command>\r
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">..\..\include\unicode\%(Filename)%(Extension);%(Outputs)</Outputs>\r
</CustomBuild>\r
+ <ClInclude Include="unifiedcache.h" />\r
<ClInclude Include="uresdata.h" />\r
<ClInclude Include="uresimp.h" />\r
<ClInclude Include="ureslocs.h" />\r
<ClCompile Include="ulist.c">\r
<Filter>collections</Filter>\r
</ClCompile>\r
+ <ClCompile Include="unifiedcache.cpp">\r
+ <Filter>collections</Filter>\r
+ </ClCompile>\r
<ClCompile Include="ustack.cpp">\r
<Filter>collections</Filter>\r
</ClCompile>\r
<ClInclude Include="ulist.h">\r
<Filter>collections</Filter>\r
</ClInclude>\r
+ <ClInclude Include="unifiedcache.h">\r
+ <Filter>collections</Filter>\r
+ </ClInclude>\r
<ClInclude Include="ustrenum.h">\r
<Filter>collections</Filter>\r
</ClInclude>\r
void
SharedObject::addRef() const {
- umtx_atomic_inc(&refCount);
+ umtx_atomic_inc(&totalRefCount);
}
void
SharedObject::removeRef() const {
- if(umtx_atomic_dec(&refCount) == 0) {
+ if(umtx_atomic_dec(&totalRefCount) == 0) {
delete this;
}
}
+void
+SharedObject::addSoftRef() const {
+ addRef();
+ umtx_atomic_inc(&softRefCount);
+}
+
+void
+SharedObject::removeSoftRef() const {
+ umtx_atomic_dec(&softRefCount);
+ removeRef();
+}
+
+UBool
+SharedObject::allSoftReferences() const {
+ return umtx_loadAcquire(totalRefCount) == umtx_loadAcquire(softRefCount);
+}
+
int32_t
SharedObject::getRefCount() const {
- return umtx_loadAcquire(refCount);
+ return umtx_loadAcquire(totalRefCount);
+}
+
+int32_t
+SharedObject::getSoftRefCount() const {
+ return umtx_loadAcquire(softRefCount);
}
void
*/
class U_COMMON_API SharedObject : public UObject {
public:
- /** Initializes refCount to 0. */
- SharedObject() : refCount(0) {}
+ /** Initializes totalRefCount, softRefCount to 0. */
+ SharedObject() : totalRefCount(0), softRefCount(0) {}
+
+ /** Initializes totalRefCount, softRefCount to 0. */
+ SharedObject(const SharedObject &other)
+ : UObject(other),
+ totalRefCount(0),
+ softRefCount(0) {}
- /** Initializes refCount to 0. */
- SharedObject(const SharedObject &other) : UObject(other), refCount(0) {}
virtual ~SharedObject();
/**
void addRef() const;
/**
- * Decrements the number of references to this object,
- * and auto-deletes "this" if the number becomes 0. Thread-safe.
+ * Increments the number of soft references to this object. Thread-safe.
+ */
+ void addSoftRef() const;
+
+ /**
+ * Decrements the number of references to this object. Thread-safe.
*/
void removeRef() const;
/**
- * Returns the reference counter. Uses a memory barrier.
+ * Decrements the number of soft references to this object. Thread-safe.
+ */
+ void removeSoftRef() const;
+
+ /**
+ * Returns the reference counter including soft references.
+ * Uses a memory barrier.
*/
int32_t getRefCount() const;
+ /**
+ * Returns the count of soft references only. Uses a memory barrier.
+ * Used for testing the cache. Regular clients won't need this.
+ */
+ int32_t getSoftRefCount() const;
+
+ /**
+ * If allSoftReferences() == TRUE then this object has only soft
+ * references. The converse is not necessarily true.
+ */
+ UBool allSoftReferences() const;
+
+ /**
+ * Deletes this object if it has no references or soft references.
+ */
void deleteIfZeroRefCount() const;
/**
}
private:
- mutable u_atomic_int32_t refCount;
+ mutable u_atomic_int32_t totalRefCount;
+ mutable u_atomic_int32_t softRefCount;
};
U_NAMESPACE_END
UCLN_COMMON_BREAKITERATOR,
UCLN_COMMON_BREAKITERATOR_DICT,
UCLN_COMMON_SERVICE,
- UCLN_COMMON_URES,
UCLN_COMMON_LOCALE,
UCLN_COMMON_LOCALE_AVAILABLE,
UCLN_COMMON_ULOC,
UCLN_COMMON_PUTIL,
UCLN_COMMON_LIST_FORMATTER,
UCLN_COMMON_UINIT,
+
+ /*
+ Unified caches caches collation stuff. Collation data structures
+ contain resource bundles which means that unified cache cleanup
+ must happen before resource bundle clean up.
+ */
+ UCLN_COMMON_UNIFIED_CACHE,
+ UCLN_COMMON_URES,
UCLN_COMMON_COUNT /* This must be last */
} ECleanupCommonType;
--- /dev/null
+/*
+******************************************************************************
+* Copyright (C) 2014, International Business Machines Corporation and
+* others. All Rights Reserved.
+******************************************************************************
+*
+* File UNIFIEDCACHE.CPP
+******************************************************************************
+*/
+
+#include "uhash.h"
+#include "unifiedcache.h"
+#include "umutex.h"
+#include "mutex.h"
+#include "uassert.h"
+#include "ucln_cmn.h"
+
+static icu::UnifiedCache *gCache = NULL;
+static icu::SharedObject *gNoValue = NULL;
+static UMutex gCacheMutex = U_MUTEX_INITIALIZER;
+static UConditionVar gInProgressValueAddedCond = U_CONDITION_INITIALIZER;
+static icu::UInitOnce gCacheInitOnce = U_INITONCE_INITIALIZER;
+
+U_CDECL_BEGIN
+static UBool U_CALLCONV unifiedcache_cleanup() {
+ gCacheInitOnce.reset();
+ if (gCache) {
+ delete gCache;
+ gCache = NULL;
+ }
+ if (gNoValue) {
+ delete gNoValue;
+ gNoValue = NULL;
+ }
+ return TRUE;
+}
+U_CDECL_END
+
+
+U_NAMESPACE_BEGIN
+
+U_CAPI int32_t U_EXPORT2
+ucache_hashKeys(const UHashTok key) {
+ const CacheKeyBase *ckey = (const CacheKeyBase *) key.pointer;
+ return ckey->hashCode();
+}
+
+U_CAPI UBool U_EXPORT2
+ucache_compareKeys(const UHashTok key1, const UHashTok key2) {
+ const CacheKeyBase *p1 = (const CacheKeyBase *) key1.pointer;
+ const CacheKeyBase *p2 = (const CacheKeyBase *) key2.pointer;
+ return *p1 == *p2;
+}
+
+U_CAPI void U_EXPORT2
+ucache_deleteKey(void *obj) {
+ CacheKeyBase *p = (CacheKeyBase *) obj;
+ delete p;
+}
+
+CacheKeyBase::~CacheKeyBase() {
+}
+
+static void U_CALLCONV cacheInit(UErrorCode &status) {
+ U_ASSERT(gCache == NULL);
+ ucln_common_registerCleanup(
+ UCLN_COMMON_UNIFIED_CACHE, unifiedcache_cleanup);
+
+ // gNoValue must be created first to avoid assertion error in
+ // cache constructor.
+ gNoValue = new SharedObject();
+ gCache = new UnifiedCache(status);
+ if (gCache == NULL) {
+ status = U_MEMORY_ALLOCATION_ERROR;
+ }
+ if (U_FAILURE(status)) {
+ delete gCache;
+ delete gNoValue;
+ gCache = NULL;
+ gNoValue = NULL;
+ return;
+ }
+ // We add a softref because we want hash elements with gNoValue to be
+ // elligible for purging but we don't ever want gNoValue to be deleted.
+ gNoValue->addSoftRef();
+}
+
+const UnifiedCache *UnifiedCache::getInstance(UErrorCode &status) {
+ umtx_initOnce(gCacheInitOnce, &cacheInit, status);
+ if (U_FAILURE(status)) {
+ return NULL;
+ }
+ U_ASSERT(gCache != NULL);
+ return gCache;
+}
+
+UnifiedCache::UnifiedCache(UErrorCode &status) {
+ if (U_FAILURE(status)) {
+ return;
+ }
+ U_ASSERT(gNoValue != NULL);
+ fHashtable = uhash_open(
+ &ucache_hashKeys,
+ &ucache_compareKeys,
+ NULL,
+ &status);
+ if (U_FAILURE(status)) {
+ return;
+ }
+ uhash_setKeyDeleter(fHashtable, &ucache_deleteKey);
+}
+
+int32_t UnifiedCache::keyCount() const {
+ Mutex lock(&gCacheMutex);
+ return uhash_count(fHashtable);
+}
+
+void UnifiedCache::flush() const {
+ Mutex lock(&gCacheMutex);
+
+ // Use a loop in case cache items that are flushed held hard references to
+ // other cache items making those additional cache items eligible for
+ // flushing.
+ while (_flush(FALSE));
+ umtx_condBroadcast(&gInProgressValueAddedCond);
+}
+
+void UnifiedCache::dump() {
+ UErrorCode status = U_ZERO_ERROR;
+ const UnifiedCache *cache = getInstance(status);
+ if (U_FAILURE(status)) {
+ fprintf(stderr, "Unified Cache: Error fetching cache.\n");
+ return;
+ }
+ cache->dumpContents();
+}
+
+void UnifiedCache::dumpContents() const {
+ Mutex lock(&gCacheMutex);
+ _dumpContents();
+}
+
+// Dumps content of cache.
+// On entry, gCacheMutex must be held.
+// On exit, cache contents dumped to stderr.
+void UnifiedCache::_dumpContents() const {
+ int32_t pos = -1;
+ const UHashElement *element = uhash_nextElement(fHashtable, &pos);
+ char buffer[256];
+ int32_t cnt = 0;
+ for (; element != NULL; element = uhash_nextElement(fHashtable, &pos)) {
+ const SharedObject *sharedObject =
+ (const SharedObject *) element->value.pointer;
+ const CacheKeyBase *key =
+ (const CacheKeyBase *) element->key.pointer;
+ if (!sharedObject->allSoftReferences()) {
+ ++cnt;
+ fprintf(
+ stderr,
+ "Unified Cache: Key '%s', error %d, value %p, total refcount %d, soft refcount %d\n",
+ key->writeDescription(buffer, 256),
+ key->creationStatus,
+ sharedObject == gNoValue ? NULL :sharedObject,
+ sharedObject->getRefCount(),
+ sharedObject->getSoftRefCount());
+ }
+ }
+ fprintf(stderr, "Unified Cache: %d out of a total of %d still have hard references\n", cnt, uhash_count(fHashtable));
+}
+
+UnifiedCache::~UnifiedCache() {
+ // Try our best to clean up first.
+ flush();
+ {
+ // Now all that should be left in the cache are entries that refer to
+ // each other and entries with hard references from outside the cache.
+ // Nothing we can do about these so proceed to wipe out the cache.
+ Mutex lock(&gCacheMutex);
+ _flush(TRUE);
+ }
+ uhash_close(fHashtable);
+}
+
+// Flushes the contents of the cache. If cache values hold references to other
+// cache values then _flush should be called in a loop until it returns FALSE.
+// On entry, gCacheMutex must be held.
+// On exit, those values with only soft references are flushed. If all is true
+// then every value is flushed even if hard references are held.
+// Returns TRUE if any value in cache was flushed or FALSE otherwise.
+UBool UnifiedCache::_flush(UBool all) const {
+ UBool result = FALSE;
+ int32_t pos = -1;
+ const UHashElement *element = uhash_nextElement(fHashtable, &pos);
+ for (; element != NULL; element = uhash_nextElement(fHashtable, &pos)) {
+ const SharedObject *sharedObject =
+ (const SharedObject *) element->value.pointer;
+ if (all || sharedObject->allSoftReferences()) {
+ uhash_removeElement(fHashtable, element);
+ sharedObject->removeSoftRef();
+ result = TRUE;
+ }
+ }
+ return result;
+}
+
+// Places a new value and creationStatus in the cache for the given key.
+// On entry, gCacheMutex must be held. key must not exist in the cache.
+// On exit, value and creation status placed under key. Soft reference added
+// to value on successful add. On error sets status.
+void UnifiedCache::_putNew(
+ const CacheKeyBase &key,
+ const SharedObject *value,
+ const UErrorCode creationStatus,
+ UErrorCode &status) const {
+ if (U_FAILURE(status)) {
+ return;
+ }
+ CacheKeyBase *keyToAdopt = key.clone();
+ if (keyToAdopt == NULL) {
+ status = U_MEMORY_ALLOCATION_ERROR;
+ return;
+ }
+ keyToAdopt->creationStatus = creationStatus;
+ uhash_put(fHashtable, keyToAdopt, (void *) value, &status);
+ if (U_SUCCESS(status)) {
+ value->addSoftRef();
+ }
+}
+
+// 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;
+}
+
+// Gets value out of cache.
+// On entry. gCacheMutex must not be held. value must be NULL. status
+// must be U_ZERO_ERROR.
+// On exit. value and status set to what is in cache at key or on cache
+// miss the key's createObject() is called and value and status are set to
+// the result of that. In this latter case, best effort is made to add the
+// value and status to the cache. value will be set to NULL instead of
+// gNoValue. Caller must call removeRef on value if non NULL.
+void UnifiedCache::_get(
+ const CacheKeyBase &key,
+ const SharedObject *&value,
+ const void *creationContext,
+ UErrorCode &status) const {
+ U_ASSERT(value == NULL);
+ U_ASSERT(status == U_ZERO_ERROR);
+ if (_poll(key, value, status)) {
+ if (value == gNoValue) {
+ SharedObject::clearPtr(value);
+ }
+ return;
+ }
+ if (U_FAILURE(status)) {
+ return;
+ }
+ value = key.createObject(creationContext, status);
+ U_ASSERT(value == NULL || !value->allSoftReferences());
+ U_ASSERT(value != NULL || status != U_ZERO_ERROR);
+ if (value == NULL) {
+ SharedObject::copyPtr(gNoValue, value);
+ }
+ _putIfAbsentAndGet(key, value, status);
+ if (value == gNoValue) {
+ SharedObject::clearPtr(value);
+ }
+}
+
+// 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);
+}
+
+// Fetch value and error code from a particular hash entry.
+// On entry, gCacheMutex must be held. value must be either NULL or must be
+// included in the ref count of the object to which it points.
+// On exit, value and status set to what is in the hash entry. Caller must
+// eventually call removeRef on value.
+// If hash entry is in progress, value will be set to gNoValue and status will
+// be set to U_ZERO_ERROR.
+void UnifiedCache::_fetch(
+ const UHashElement *element,
+ const SharedObject *&value,
+ UErrorCode &status) {
+ const CacheKeyBase *theKey = (const CacheKeyBase *) element->key.pointer;
+ status = theKey->creationStatus;
+ SharedObject::copyPtr(
+ (const SharedObject *) element->value.pointer, value);
+}
+
+// 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 = (value == gNoValue && status == U_ZERO_ERROR);
+ SharedObject::clearPtr(value);
+ return result;
+}
+
+U_NAMESPACE_END
--- /dev/null
+/*
+******************************************************************************
+* Copyright (C) 2014, International Business Machines Corporation and
+* others. All Rights Reserved.
+******************************************************************************
+*
+* File UNIFIEDCACHE.H - The ICU Unified cache.
+******************************************************************************
+*/
+
+#ifndef __UNIFIED_CACHE_H__
+#define __UNIFIED_CACHE_H__
+
+#include "utypeinfo.h" // for 'typeid' to work
+
+#include "unicode/uobject.h"
+#include "unicode/locid.h"
+#include "sharedobject.h"
+#include "unicode/unistr.h"
+#include "cstring.h"
+#include "ustr_imp.h"
+
+struct UHashtable;
+struct UHashElement;
+
+U_NAMESPACE_BEGIN
+
+class UnifiedCache;
+
+/**
+ * A base class for all cache keys
+ */
+class U_COMMON_API CacheKeyBase : public UObject {
+ public:
+ CacheKeyBase() : creationStatus(U_ZERO_ERROR) {}
+
+ /**
+ * Copy constructor. Needed to support cloning.
+ */
+ CacheKeyBase(const CacheKeyBase &other)
+ : creationStatus(other.creationStatus) { }
+ virtual ~CacheKeyBase();
+
+ /**
+ * Returns the hash code for this object.
+ */
+ virtual int32_t hashCode() const = 0;
+
+ /**
+ * Clones this object polymorphically. Caller owns returned value.
+ */
+ virtual CacheKeyBase *clone() const = 0;
+
+ /**
+ * Equality operator.
+ */
+ virtual UBool operator == (const CacheKeyBase &other) const = 0;
+
+ /**
+ * Create a new object for this key. Called by cache on cache miss.
+ * createObject must add a reference to the object it returns. Note
+ * that getting an object from the cache and returning it without calling
+ * removeRef on it satisfies this requirement. It can also return NULL
+ * and set status to an error.
+ *
+ * @param creationContext the context in which the object is being
+ * created. May be NULL.
+ * @param status Implementations can return a failure here.
+ * In addition, implementations may return a
+ * non NULL object and set a warning status.
+ */
+ virtual const SharedObject *createObject(
+ const void *creationContext, UErrorCode &status) const = 0;
+
+ /**
+ * Writes a description of this key to buffer and returns buffer. Written
+ * description is NULL terminated.
+ */
+ virtual char *writeDescription(char *buffer, int32_t bufSize) const = 0;
+
+ /**
+ * Inequality operator.
+ */
+ UBool operator != (const CacheKeyBase &other) const {
+ return !(*this == other);
+ }
+ private:
+ mutable UErrorCode creationStatus;
+ friend class UnifiedCache;
+};
+
+
+
+/**
+ * Templated version of CacheKeyBase.
+ * A key of type LocaleCacheKey<T> maps to a value of type T.
+ */
+template<typename T>
+class CacheKey : public CacheKeyBase {
+ public:
+ virtual ~CacheKey() { }
+ /**
+ * The template parameter, T, determines the hash code returned.
+ */
+ virtual int32_t hashCode() const {
+ const char *s = typeid(T).name();
+ return ustr_hashCharsN(s, uprv_strlen(s));
+ }
+
+ /**
+ * Use the value type, T, as the description.
+ */
+ virtual char *writeDescription(char *buffer, int32_t bufLen) const {
+ const char *s = typeid(T).name();
+ uprv_strncpy(buffer, s, bufLen);
+ buffer[bufLen - 1] = 0;
+ return buffer;
+ }
+
+ /**
+ * Two objects are equal if they are of the same type.
+ */
+ virtual UBool operator == (const CacheKeyBase &other) const {
+ return typeid(*this) == typeid(other);
+ }
+};
+
+/**
+ * Cache key based on locale.
+ * A key of type LocaleCacheKey<T> maps to a value of type T.
+ */
+template<typename T>
+class LocaleCacheKey : public CacheKey<T> {
+ protected:
+ Locale fLoc;
+ public:
+ LocaleCacheKey(const Locale &loc) : fLoc(loc) {};
+ virtual ~LocaleCacheKey() { }
+ virtual int32_t hashCode() const {
+ return 37 *CacheKey<T>::hashCode() + fLoc.hashCode();
+ }
+ virtual UBool operator == (const CacheKeyBase &other) const {
+ // reflexive
+ if (this == &other) {
+ return TRUE;
+ }
+ if (!CacheKey<T>::operator == (other)) {
+ return FALSE;
+ }
+ // We know this and other are of same class because operator== on
+ // CacheKey returned true.
+ const LocaleCacheKey<T> *fOther =
+ static_cast<const LocaleCacheKey<T> *>(&other);
+ return fLoc == fOther->fLoc;
+ }
+ virtual CacheKeyBase *clone() const {
+ return new LocaleCacheKey<T>(*this);
+ }
+ virtual const T *createObject(
+ const void *creationContext, UErrorCode &status) const;
+ /**
+ * Use the locale id as the description.
+ */
+ virtual char *writeDescription(char *buffer, int32_t bufLen) const {
+ const char *s = fLoc.getName();
+ uprv_strncpy(buffer, s, bufLen);
+ buffer[bufLen - 1] = 0;
+ return buffer;
+ }
+
+};
+
+/**
+ * The unified cache. A singleton type.
+ */
+class U_COMMON_API UnifiedCache : public UObject {
+ public:
+ /**
+ * @internal
+ */
+ UnifiedCache(UErrorCode &status);
+
+ /**
+ * Returns the cache instance.
+ */
+ static const UnifiedCache *getInstance(UErrorCode &status);
+
+ /**
+ * Fetches a value from the cache by key. Equivalent to
+ * get(key, NULL, ptr, status);
+ */
+ template<typename T>
+ void get(
+ const CacheKey<T>& key,
+ const T *&ptr,
+ UErrorCode &status) const {
+ get(key, NULL, ptr, status);
+ }
+
+ /**
+ * Fetches value from the cache by key.
+ *
+ * @param key the cache key.
+ * @param creationContext passed verbatim to createObject method of key
+ * @param ptr On entry, ptr must be NULL or be included if
+ * the reference count of the object it points
+ * to. On exit, ptr points to the fetched object
+ * from the cache or is left unchanged on
+ * failure. Caller must call removeRef on ptr
+ * if set to a non NULL value.
+ * @param status Any error returned here. May be set to a
+ * warning value even if ptr is set.
+ */
+ template<typename T>
+ void get(
+ const CacheKey<T>& key,
+ const void *creationContext,
+ const T *&ptr,
+ UErrorCode &status) const {
+ if (U_FAILURE(status)) {
+ return;
+ }
+ UErrorCode creationStatus = U_ZERO_ERROR;
+ const SharedObject *value = NULL;
+ _get(key, value, creationContext, creationStatus);
+ const T *tvalue = (const T *) value;
+ if (U_SUCCESS(creationStatus)) {
+ SharedObject::copyPtr(tvalue, ptr);
+ }
+ SharedObject::clearPtr(tvalue);
+ // Take care not to overwrite a warning status passed in with
+ // another warning or U_ZERO_ERROR.
+ if (status == U_ZERO_ERROR || U_FAILURE(creationStatus)) {
+ status = creationStatus;
+ }
+ }
+
+ /**
+ * Dumps the contents of this cache to standard error. Used for testing of
+ * cache only.
+ */
+ void dumpContents() const;
+
+ /**
+ * Convenience method to get a value of type T from cache for a
+ * particular locale with creationContext == NULL.
+ * @param loc the locale
+ * @param ptr On entry, must be NULL or included in the ref count
+ * of the object to which it points.
+ * On exit, fetched value stored here or is left
+ * unchanged on failure. Caller must call removeRef on
+ * ptr if set to a non NULL value.
+ * @param status Any error returned here. May be set to a
+ * warning value even if ptr is set.
+ */
+ template<typename T>
+ static void getByLocale(
+ const Locale &loc, const T *&ptr, UErrorCode &status) {
+ const UnifiedCache *cache = getInstance(status);
+ if (U_FAILURE(status)) {
+ return;
+ }
+ cache->get(LocaleCacheKey<T>(loc), ptr, status);
+ }
+
+ /**
+ * Dumps the cache contents to stderr. For testing only.
+ */
+ static void dump();
+
+ /**
+ * Returns the number of keys in this cache. For testing only.
+ */
+ int32_t keyCount() const;
+
+ /**
+ * Removes any values from cache that are not referenced outside
+ * the cache.
+ */
+ void flush() const;
+
+ virtual ~UnifiedCache();
+ private:
+ UHashtable *fHashtable;
+ UnifiedCache(const UnifiedCache &other);
+ UnifiedCache &operator=(const UnifiedCache &other);
+ UBool _flush(UBool all) const;
+ void _get(
+ const CacheKeyBase &key,
+ const SharedObject *&value,
+ const void *creationContext,
+ UErrorCode &status) const;
+ UBool _poll(
+ const CacheKeyBase &key,
+ const SharedObject *&value,
+ UErrorCode &status) const;
+ void _putNew(
+ const CacheKeyBase &key,
+ const SharedObject *value,
+ const UErrorCode creationStatus,
+ UErrorCode &status) const;
+ void _putIfAbsentAndGet(
+ const CacheKeyBase &key,
+ const SharedObject *&value,
+ UErrorCode &status) const;
+ void _dumpContents() const;
+ static void _put(
+ const UHashElement *element,
+ const SharedObject *value,
+ const UErrorCode status);
+ static void _fetch(
+ const UHashElement *element,
+ const SharedObject *&value,
+ UErrorCode &status);
+ static UBool _inProgress(const UHashElement *element);
+};
+
+U_NAMESPACE_END
+
+#endif
}
-Collator* Collator::makeInstance(const Locale& desiredLocale,
- UErrorCode& status)
-{
- Locale validLocale("");
- const CollationTailoring *t =
- CollationLoader::loadTailoring(desiredLocale, validLocale, status);
+Collator* Collator::makeInstance(const Locale& desiredLocale, UErrorCode& status) {
+ const CollationCacheEntry *entry = CollationLoader::loadTailoring(desiredLocale, status);
if (U_SUCCESS(status)) {
- Collator *result = new RuleBasedCollator(t, validLocale);
+ Collator *result = new RuleBasedCollator(entry);
if (result != NULL) {
+ // Both the unified cache's get() and the RBC constructor
+ // did addRef(). Undo one of them.
+ entry->removeRef();
return result;
}
status = U_MEMORY_ALLOCATION_ERROR;
}
- if (t != NULL) {
- t->deleteIfZeroRefCount();
+ if (entry != NULL) {
+ // Undo the addRef() from the cache.get().
+ entry->removeRef();
}
return NULL;
}
: data(NULL),
settings(NULL),
tailoring(NULL),
+ cacheEntry(NULL),
validLocale(""),
explicitlySetAttributes(0),
actualLocaleIsSameAsValid(FALSE) {
: data(NULL),
settings(NULL),
tailoring(NULL),
+ cacheEntry(NULL),
validLocale(""),
explicitlySetAttributes(0),
actualLocaleIsSameAsValid(FALSE) {
: data(NULL),
settings(NULL),
tailoring(NULL),
+ cacheEntry(NULL),
validLocale(""),
explicitlySetAttributes(0),
actualLocaleIsSameAsValid(FALSE) {
: data(NULL),
settings(NULL),
tailoring(NULL),
+ cacheEntry(NULL),
validLocale(""),
explicitlySetAttributes(0),
actualLocaleIsSameAsValid(FALSE) {
: data(NULL),
settings(NULL),
tailoring(NULL),
+ cacheEntry(NULL),
validLocale(""),
explicitlySetAttributes(0),
actualLocaleIsSameAsValid(FALSE) {
: data(NULL),
settings(NULL),
tailoring(NULL),
+ cacheEntry(NULL),
validLocale(""),
explicitlySetAttributes(0),
actualLocaleIsSameAsValid(FALSE) {
return;
}
t->actualLocale.setToBogus();
- adoptTailoring(t.orphan());
+ adoptTailoring(t.orphan(), errorCode);
// Set attributes after building the collator,
// to keep the default settings consistent with the rule string.
if(strength != UCOL_DEFAULT) {
namespace {
-static const CollationTailoring *rootSingleton = NULL;
+static const CollationCacheEntry *rootSingleton = NULL;
static UInitOnce initOnce = U_INITONCE_INITIALIZER;
} // namespace
CollationDataReader::read(NULL, inBytes, udata_getLength(t->memory), *t, errorCode);
if(U_FAILURE(errorCode)) { return; }
ucln_i18n_registerCleanup(UCLN_I18N_COLLATION_ROOT, uprv_collation_root_cleanup);
- t->addRef(); // The rootSingleton takes ownership.
- rootSingleton = t.orphan();
+ CollationCacheEntry *entry = new CollationCacheEntry(Locale::getRoot(), t.getAlias());
+ if(entry != NULL) {
+ t.orphan(); // The rootSingleton took ownership of the tailoring.
+ entry->addRef();
+ rootSingleton = entry;
+ }
+}
+
+const CollationCacheEntry *
+CollationRoot::getRootCacheEntry(UErrorCode &errorCode) {
+ umtx_initOnce(initOnce, CollationRoot::load, errorCode);
+ if(U_FAILURE(errorCode)) { return NULL; }
+ return rootSingleton;
}
const CollationTailoring *
CollationRoot::getRoot(UErrorCode &errorCode) {
umtx_initOnce(initOnce, CollationRoot::load, errorCode);
if(U_FAILURE(errorCode)) { return NULL; }
- return rootSingleton;
+ return rootSingleton->tailoring;
}
const CollationData *
U_NAMESPACE_BEGIN
+struct CollationCacheEntry;
struct CollationData;
struct CollationSettings;
struct CollationTailoring;
*/
class U_I18N_API CollationRoot { // purely static
public:
+ static const CollationCacheEntry *getRootCacheEntry(UErrorCode &errorCode);
static const CollationTailoring *getRoot(UErrorCode &errorCode);
static const CollationData *getData(UErrorCode &errorCode);
static const CollationSettings *getSettings(UErrorCode &errorCode);
return ((int32_t)version[1] << 4) | (version[2] >> 6);
}
+CollationCacheEntry::~CollationCacheEntry() {
+ SharedObject::clearPtr(tailoring);
+}
+
U_NAMESPACE_END
#endif // !UCONFIG_NO_COLLATION
CollationTailoring(const CollationTailoring &other);
};
+struct CollationCacheEntry : public SharedObject {
+ CollationCacheEntry(const Locale &loc, const CollationTailoring *t)
+ : validLocale(loc), tailoring(t) {
+ if(t != NULL) {
+ t->addRef();
+ }
+ }
+ ~CollationCacheEntry();
+
+ Locale validLocale;
+ const CollationTailoring *tailoring;
+};
+
U_NAMESPACE_END
#endif // !UCONFIG_NO_COLLATION
#include "quantityformatter.h"
#include "unicode/plurrule.h"
#include "unicode/decimfmt.h"
-#include "lrucache.h"
#include "uresimp.h"
#include "unicode/ures.h"
#include "cstring.h"
#include "sharednumberformat.h"
#include "sharedpluralrules.h"
+#include "unifiedcache.h"
#define LENGTHOF(array) (int32_t)(sizeof(array)/sizeof((array)[0]))
#define MEAS_UNIT_COUNT 46
#define WIDTH_INDEX_COUNT (UMEASFMT_WIDTH_NARROW + 1)
-static icu::LRUCache *gCache = NULL;
-static UMutex gCacheMutex = U_MUTEX_INITIALIZER;
-static icu::UInitOnce gCacheInitOnce = U_INITONCE_INITIALIZER;
-
-U_CDECL_BEGIN
-static UBool U_CALLCONV measfmt_cleanup() {
- gCacheInitOnce.reset();
- if (gCache) {
- delete gCache;
- gCache = NULL;
- }
- return TRUE;
-}
-U_CDECL_END
-
U_NAMESPACE_BEGIN
UOBJECT_DEFINE_RTTI_IMPLEMENTATION(MeasureFormat)
return result;
}
-// Creates the MeasureFormatCacheData for a particular locale
-static SharedObject *U_CALLCONV createData(
- const char *localeId, UErrorCode &status) {
+template<> U_I18N_API
+const MeasureFormatCacheData *LocaleCacheKey<MeasureFormatCacheData>::createObject(
+ const void * /*unused*/, UErrorCode &status) const {
+ const char *localeId = fLoc.getName();
LocalUResourceBundlePointer topLevel(ures_open(NULL, localeId, &status));
static UNumberFormatStyle currencyStyles[] = {
UNUM_CURRENCY_PLURAL, UNUM_CURRENCY_ISO, UNUM_CURRENCY};
decfmt->setRoundingMode(DecimalFormat::kRoundDown);
}
result->adoptIntegerFormat(inf);
+ result->addRef();
return result.orphan();
}
-static void U_CALLCONV cacheInit(UErrorCode &status) {
- U_ASSERT(gCache == NULL);
- U_ASSERT(MeasureUnit::getIndexCount() == MEAS_UNIT_COUNT);
- ucln_i18n_registerCleanup(UCLN_I18N_MEASFMT, measfmt_cleanup);
- gCache = new SimpleLRUCache(100, &createData, status);
- if (U_FAILURE(status)) {
- delete gCache;
- gCache = NULL;
- }
-}
-
-static UBool getFromCache(
- const char *locale,
- const MeasureFormatCacheData *&ptr,
- UErrorCode &status) {
- umtx_initOnce(gCacheInitOnce, &cacheInit, status);
- if (U_FAILURE(status)) {
- return FALSE;
- }
- Mutex lock(&gCacheMutex);
- gCache->get(locale, ptr, status);
- return U_SUCCESS(status);
-}
-
static UBool isTimeUnit(const MeasureUnit &mu, const char *tu) {
return uprv_strcmp(mu.getType(), "duration") == 0 &&
uprv_strcmp(mu.getSubtype(), tu) == 0;
const char *name = locale.getName();
setLocaleIDs(name, name);
- if (!getFromCache(name, cache, status)) {
+ UnifiedCache::getByLocale(locale, cache, status);
+ if (U_FAILURE(status)) {
return;
}
#include "digitlst.h"
#include <float.h>
#include "sharednumberformat.h"
-#include "lrucache.h"
+#include "unifiedcache.h"
//#define FMT_DEBUG
"currencyFormat" // UNUM_CASH_CURRENCY
};
-static icu::LRUCache *gNumberFormatCache = NULL;
-static UMutex gNumberFormatCacheMutex = U_MUTEX_INITIALIZER;
-static icu::UInitOnce gNumberFormatCacheInitOnce = U_INITONCE_INITIALIZER;
-
// Static hashtable cache of NumberingSystem objects used by NumberFormat
static UHashtable * NumberingSystem_cache = NULL;
static UMutex nscacheMutex = U_MUTEX_INITIALIZER;
uhash_close(NumberingSystem_cache);
NumberingSystem_cache = NULL;
}
- gNumberFormatCacheInitOnce.reset();
- if (gNumberFormatCache) {
- delete gNumberFormatCache;
- gNumberFormatCache = NULL;
- }
return TRUE;
}
U_CDECL_END
uhash_setValueDeleter(NumberingSystem_cache, deleteNumberingSystem);
}
-static SharedObject *U_CALLCONV createSharedNumberFormat(
- const char *localeId, UErrorCode &status) {
- if (U_FAILURE(status)) {
- return NULL;
- }
+template<> U_I18N_API
+const SharedNumberFormat *LocaleCacheKey<SharedNumberFormat>::createObject(
+ const void * /*unused*/, UErrorCode &status) const {
+ const char *localeId = fLoc.getName();
NumberFormat *nf = NumberFormat::internalCreateInstance(
localeId, UNUM_DECIMAL, status);
if (U_FAILURE(status)) {
return NULL;
}
- SharedObject *result = new SharedNumberFormat(nf);
+ SharedNumberFormat *result = new SharedNumberFormat(nf);
if (result == NULL) {
status = U_MEMORY_ALLOCATION_ERROR;
delete nf;
return NULL;
}
+ result->addRef();
return result;
}
-static void U_CALLCONV numberFormatCacheInit(UErrorCode &status) {
- U_ASSERT(gNumberFormatCache == NULL);
- ucln_i18n_registerCleanup(UCLN_I18N_NUMFMT, numfmt_cleanup);
- gNumberFormatCache = new SimpleLRUCache(100, &createSharedNumberFormat, status);
- if (U_FAILURE(status)) {
- delete gNumberFormatCache;
- gNumberFormatCache = NULL;
- }
-}
-
-static void getSharedNumberFormatFromCache(
- const char *locale,
- const SharedNumberFormat *&ptr,
- UErrorCode &status) {
- umtx_initOnce(gNumberFormatCacheInitOnce, &numberFormatCacheInit, status);
- if (U_FAILURE(status)) {
- return;
- }
- Mutex lock(&gNumberFormatCacheMutex);
- gNumberFormatCache->get(locale, ptr, status);
-}
-
const SharedNumberFormat* U_EXPORT2
NumberFormat::createSharedInstance(const Locale& loc, UNumberFormatStyle kind, UErrorCode& status) {
if (U_FAILURE(status)) {
return NULL;
}
const SharedNumberFormat *result = NULL;
- getSharedNumberFormatFromCache(loc.getName(), result, status);
+ UnifiedCache::getByLocale(loc, result, status);
return result;
}
#include "uassert.h"
#include "uvectr32.h"
#include "sharedpluralrules.h"
-#include "lrucache.h"
+#include "unifiedcache.h"
#if !UCONFIG_NO_FORMATTING
-static icu::LRUCache *gPluralRulesCache = NULL;
-static UMutex gPluralRulesCacheMutex = U_MUTEX_INITIALIZER;
-static icu::UInitOnce gPluralRulesCacheInitOnce = U_INITONCE_INITIALIZER;
-
-U_CDECL_BEGIN
-static UBool U_CALLCONV plurrules_cleanup(void) {
- gPluralRulesCacheInitOnce.reset();
- if (gPluralRulesCache) {
- delete gPluralRulesCache;
- gPluralRulesCache = NULL;
- }
- return TRUE;
-}
-U_CDECL_END
-
U_NAMESPACE_BEGIN
#define ARRAY_SIZE(array) (int32_t)(sizeof array / sizeof array[0])
/******************************************************************************/
/* Create PluralRules cache */
-static SharedObject *U_CALLCONV createSharedPluralRules(
- const char *localeId, UErrorCode &status) {
- if (U_FAILURE(status)) {
- return NULL;
- }
+template<> U_I18N_API
+const SharedPluralRules *LocaleCacheKey<SharedPluralRules>::createObject(
+ const void * /*unused*/, UErrorCode &status) const {
+ const char *localeId = fLoc.getName();
PluralRules *pr = PluralRules::internalForLocale(
localeId, UPLURAL_TYPE_CARDINAL, status);
if (U_FAILURE(status)) {
return NULL;
}
- SharedObject *result = new SharedPluralRules(pr);
+ SharedPluralRules *result = new SharedPluralRules(pr);
if (result == NULL) {
status = U_MEMORY_ALLOCATION_ERROR;
delete pr;
return NULL;
}
+ result->addRef();
return result;
}
-static void U_CALLCONV pluralRulesCacheInit(UErrorCode &status) {
- U_ASSERT(gPluralRulesCache == NULL);
- ucln_i18n_registerCleanup(UCLN_I18N_PLURAL_RULE, plurrules_cleanup);
- gPluralRulesCache = new SimpleLRUCache(100, &createSharedPluralRules, status);
- if (U_FAILURE(status)) {
- delete gPluralRulesCache;
- gPluralRulesCache = NULL;
- }
-}
-
-static void getSharedPluralRulesFromCache(
- const char *locale,
- const SharedPluralRules *&ptr,
- UErrorCode &status) {
- umtx_initOnce(gPluralRulesCacheInitOnce, &pluralRulesCacheInit, status);
- if (U_FAILURE(status)) {
- return;
- }
- Mutex lock(&gPluralRulesCacheMutex);
- gPluralRulesCache->get(locale, ptr, status);
-}
-
-
-
-
/* end plural rules cache */
/******************************************************************************/
return NULL;
}
const SharedPluralRules *result = NULL;
- getSharedPluralRulesFromCache(locale.getName(), result, status);
+ UnifiedCache::getByLocale(locale, result, status);
return result;
}
#include "unicode/decimfmt.h"
#include "unicode/numfmt.h"
#include "unicode/brkiter.h"
-#include "lrucache.h"
#include "uresimp.h"
#include "unicode/ures.h"
#include "cstring.h"
#include "sharedbreakiterator.h"
#include "sharedpluralrules.h"
#include "sharednumberformat.h"
+#include "unifiedcache.h"
// Copied from uscript_props.cpp
#define LENGTHOF(array) (int32_t)(sizeof(array)/sizeof((array)[0]))
-static icu::LRUCache *gCache = NULL;
-static UMutex gCacheMutex = U_MUTEX_INITIALIZER;
static UMutex gBrkIterMutex = U_MUTEX_INITIALIZER;
-static icu::UInitOnce gCacheInitOnce = U_INITONCE_INITIALIZER;
-
-U_CDECL_BEGIN
-static UBool U_CALLCONV reldatefmt_cleanup() {
- gCacheInitOnce.reset();
- if (gCache) {
- delete gCache;
- gCache = NULL;
- }
- return TRUE;
-}
-U_CDECL_END
U_NAMESPACE_BEGIN
return getStringByIndex(topLevel.getAlias(), 8, result, status);
}
-// Creates RelativeDateTimeFormatter specific data for a given locale
-static SharedObject *U_CALLCONV createData(
- const char *localeId, UErrorCode &status) {
+template<> U_I18N_API
+const RelativeDateTimeCacheData *LocaleCacheKey<RelativeDateTimeCacheData>::createObject(const void * /*unused*/, UErrorCode &status) const {
+ const char *localeId = fLoc.getName();
LocalUResourceBundlePointer topLevel(ures_open(NULL, localeId, &status));
if (U_FAILURE(status)) {
return NULL;
if (U_FAILURE(status)) {
return NULL;
}
+ result->addRef();
return result.orphan();
}
-static void U_CALLCONV cacheInit(UErrorCode &status) {
- U_ASSERT(gCache == NULL);
- ucln_i18n_registerCleanup(UCLN_I18N_RELDATEFMT, reldatefmt_cleanup);
- gCache = new SimpleLRUCache(100, &createData, status);
- if (U_FAILURE(status)) {
- delete gCache;
- gCache = NULL;
- }
-}
-
-static UBool getFromCache(
- const char *locale,
- const RelativeDateTimeCacheData *&ptr,
- UErrorCode &status) {
- umtx_initOnce(gCacheInitOnce, &cacheInit, status);
- if (U_FAILURE(status)) {
- return FALSE;
- }
- Mutex lock(&gCacheMutex);
- gCache->get(locale, ptr, status);
- return U_SUCCESS(status);
-}
-
RelativeDateTimeFormatter::RelativeDateTimeFormatter(UErrorCode& status) :
fCache(NULL),
fNumberFormat(NULL),
UErrorCode &status) {
LocalPointer<NumberFormat> nf(nfToAdopt);
LocalPointer<BreakIterator> bi(biToAdopt);
- if (!getFromCache(fLocale.getName(), fCache, status)) {
+ UnifiedCache::getByLocale(fLocale, fCache, status);
+ if (U_FAILURE(status)) {
return;
}
const SharedPluralRules *pr = PluralRules::createSharedInstance(
data(other.data),
settings(other.settings),
tailoring(other.tailoring),
+ cacheEntry(other.cacheEntry),
validLocale(other.validLocale),
explicitlySetAttributes(other.explicitlySetAttributes),
actualLocaleIsSameAsValid(other.actualLocaleIsSameAsValid) {
settings->addRef();
- tailoring->addRef();
+ cacheEntry->addRef();
}
RuleBasedCollator::RuleBasedCollator(const uint8_t *bin, int32_t length,
: data(NULL),
settings(NULL),
tailoring(NULL),
+ cacheEntry(NULL),
validLocale(""),
explicitlySetAttributes(0),
actualLocaleIsSameAsValid(FALSE) {
CollationDataReader::read(base->tailoring, bin, length, *t, errorCode);
if(U_FAILURE(errorCode)) { return; }
t->actualLocale.setToBogus();
- adoptTailoring(t.orphan());
+ adoptTailoring(t.orphan(), errorCode);
}
-RuleBasedCollator::RuleBasedCollator(const CollationTailoring *t, const Locale &vl)
- : data(t->data),
- settings(t->settings),
- tailoring(t),
- validLocale(vl),
+RuleBasedCollator::RuleBasedCollator(const CollationCacheEntry *entry)
+ : data(entry->tailoring->data),
+ settings(entry->tailoring->settings),
+ tailoring(entry->tailoring),
+ cacheEntry(entry),
+ validLocale(entry->validLocale),
explicitlySetAttributes(0),
actualLocaleIsSameAsValid(FALSE) {
settings->addRef();
- tailoring->addRef();
+ cacheEntry->addRef();
}
RuleBasedCollator::~RuleBasedCollator() {
SharedObject::clearPtr(settings);
- SharedObject::clearPtr(tailoring);
+ SharedObject::clearPtr(cacheEntry);
}
void
-RuleBasedCollator::adoptTailoring(CollationTailoring *t) {
- U_ASSERT(settings == NULL && data == NULL && tailoring == NULL);
+RuleBasedCollator::adoptTailoring(CollationTailoring *t, UErrorCode &errorCode) {
+ if(U_FAILURE(errorCode)) {
+ t->deleteIfZeroRefCount();
+ return;
+ }
+ U_ASSERT(settings == NULL && data == NULL && tailoring == NULL && cacheEntry == NULL);
+ cacheEntry = new CollationCacheEntry(t->actualLocale, t);
+ if(cacheEntry == NULL) {
+ errorCode = U_MEMORY_ALLOCATION_ERROR;
+ t->deleteIfZeroRefCount();
+ return;
+ }
data = t->data;
settings = t->settings;
settings->addRef();
- t->addRef();
tailoring = t;
+ cacheEntry->addRef();
validLocale = t->actualLocale;
actualLocaleIsSameAsValid = FALSE;
}
RuleBasedCollator &RuleBasedCollator::operator=(const RuleBasedCollator &other) {
if(this == &other) { return *this; }
SharedObject::copyPtr(other.settings, settings);
- SharedObject::copyPtr(other.tailoring, tailoring);
+ tailoring = other.tailoring;
+ SharedObject::copyPtr(other.cacheEntry, cacheEntry);
data = tailoring->data;
validLocale = other.validLocale;
explicitlySetAttributes = other.explicitlySetAttributes;
UCLN_I18N_TIMEZONENAMES,
UCLN_I18N_ZONEMETA,
UCLN_I18N_TIMEZONE,
- UCLN_I18N_PLURAL_RULE,
UCLN_I18N_CURRENCY,
UCLN_I18N_DECFMT,
UCLN_I18N_NUMFMT,
- UCLN_I18N_RELDATEFMT,
- UCLN_I18N_MEASFMT,
UCLN_I18N_SMPDTFMT,
UCLN_I18N_USEARCH,
UCLN_I18N_COLLATOR,
#if !UCONFIG_NO_COLLATION
+// This part needs to compile as plain C code, for cintltst.
+
#include "unicode/ucol.h"
/** Check whether two collators are equal. Collators are considered equal if they
#ifdef __cplusplus
+#include "unicode/locid.h"
+#include "unicode/ures.h"
+
U_NAMESPACE_BEGIN
-struct CollationTailoring;
+struct CollationCacheEntry;
class Locale;
class UnicodeString;
+class UnifiedCache;
/** Implemented in ucol_res.cpp. */
class CollationLoader {
static void appendRootRules(UnicodeString &s);
static void loadRules(const char *localeID, const char *collationType,
UnicodeString &rules, UErrorCode &errorCode);
- static const CollationTailoring *loadTailoring(const Locale &locale, Locale &validLocale,
- UErrorCode &errorCode);
+ // Adds a reference to returned value.
+ static const CollationCacheEntry *loadTailoring(const Locale &locale, UErrorCode &errorCode);
+
+ // Cache callback. Adds a reference to returned value.
+ const CollationCacheEntry *createCacheEntry(UErrorCode &errorCode);
private:
- CollationLoader(); // not implemented, all methods are static
static void loadRootRules(UErrorCode &errorCode);
+
+ // The following members are used by loadTailoring()
+ // and the cache callback.
+ static const uint32_t TRIED_SEARCH = 1;
+ static const uint32_t TRIED_DEFAULT = 2;
+ static const uint32_t TRIED_STANDARD = 4;
+
+ CollationLoader(const CollationCacheEntry *re, const Locale &requested, UErrorCode &errorCode);
+ ~CollationLoader();
+
+ // All loadFromXXX methods add a reference to the returned value.
+ const CollationCacheEntry *loadFromLocale(UErrorCode &errorCode);
+ const CollationCacheEntry *loadFromBundle(UErrorCode &errorCode);
+ const CollationCacheEntry *loadFromCollations(UErrorCode &errorCode);
+ const CollationCacheEntry *loadFromData(UErrorCode &errorCode);
+
+ // Adds a reference to returned value.
+ const CollationCacheEntry *getCacheEntry(UErrorCode &errorCode);
+
+ /**
+ * Returns the rootEntry (with one addRef()) if loc==root,
+ * or else returns a new cache entry with ref count 1 for the loc and
+ * the root tailoring.
+ */
+ const CollationCacheEntry *makeCacheEntryFromRoot(
+ const Locale &loc, UErrorCode &errorCode) const;
+
+ /**
+ * Returns the entryFromCache as is if loc==validLocale,
+ * or else returns a new cache entry with ref count 1 for the loc and
+ * the same tailoring. In the latter case, a ref count is removed from
+ * entryFromCache.
+ */
+ static const CollationCacheEntry *makeCacheEntry(
+ const Locale &loc,
+ const CollationCacheEntry *entryFromCache,
+ UErrorCode &errorCode);
+
+ const UnifiedCache *cache;
+ const CollationCacheEntry *rootEntry;
+ Locale validLocale;
+ Locale locale;
+ char type[16];
+ char defaultType[16];
+ uint32_t typesTried;
+ UBool typeFallback;
+ UResourceBundle *bundle;
+ UResourceBundle *collations;
+ UResourceBundle *data;
};
U_NAMESPACE_END
#include "uenumimp.h"
#include "ulist.h"
#include "umutex.h"
+#include "unifiedcache.h"
#include "uresimp.h"
#include "ustrenum.h"
#include "utracimp.h"
}
}
-const CollationTailoring *
-CollationLoader::loadTailoring(const Locale &locale, Locale &validLocale, UErrorCode &errorCode) {
- const CollationTailoring *root = CollationRoot::getRoot(errorCode);
+template<> U_I18N_API
+const CollationCacheEntry *
+LocaleCacheKey<CollationCacheEntry>::createObject(const void *creationContext,
+ UErrorCode &errorCode) const {
+ CollationLoader *loader =
+ reinterpret_cast<CollationLoader *>(
+ const_cast<void *>(creationContext));
+ return loader->createCacheEntry(errorCode);
+}
+
+const CollationCacheEntry *
+CollationLoader::loadTailoring(const Locale &locale, UErrorCode &errorCode) {
+ const CollationCacheEntry *rootEntry = CollationRoot::getRootCacheEntry(errorCode);
if(U_FAILURE(errorCode)) { return NULL; }
const char *name = locale.getName();
if(*name == 0 || uprv_strcmp(name, "root") == 0) {
- validLocale = Locale::getRoot();
- return root;
+
+ // Have to add a ref.
+ rootEntry->addRef();
+ return rootEntry;
+ }
+
+ // Clear warning codes before loading where they get cached.
+ errorCode = U_ZERO_ERROR;
+ CollationLoader loader(rootEntry, locale, errorCode);
+
+ // getCacheEntry adds a ref for us.
+ return loader.getCacheEntry(errorCode);
+}
+
+CollationLoader::CollationLoader(const CollationCacheEntry *re, const Locale &requested,
+ UErrorCode &errorCode)
+ : cache(UnifiedCache::getInstance(errorCode)), rootEntry(re),
+ validLocale(re->validLocale), locale(requested),
+ typesTried(0), typeFallback(FALSE),
+ bundle(NULL), collations(NULL), data(NULL) {
+ type[0] = 0;
+ defaultType[0] = 0;
+ if(U_FAILURE(errorCode)) { return; }
+
+ // Canonicalize the locale ID: Ignore all irrelevant keywords.
+ const char *baseName = locale.getBaseName();
+ if(uprv_strcmp(locale.getName(), baseName) != 0) {
+ locale = Locale(baseName);
+
+ // Fetch the collation type from the locale ID.
+ int32_t typeLength = requested.getKeywordValue("collation",
+ type, LENGTHOF(type) - 1, errorCode);
+ if(U_FAILURE(errorCode)) {
+ errorCode = U_ILLEGAL_ARGUMENT_ERROR;
+ return;
+ }
+ type[typeLength] = 0; // in case of U_NOT_TERMINATED_WARNING
+ if(typeLength == 0) {
+ // No collation type.
+ } else if(uprv_stricmp(type, "default") == 0) {
+ // Ignore "default" (case-insensitive).
+ type[0] = 0;
+ } else {
+ // Copy the collation type.
+ T_CString_toLowerCase(type);
+ locale.setKeywordValue("collation", type, errorCode);
+ }
}
+}
- LocalUResourceBundlePointer bundle(ures_open(U_ICUDATA_COLL, name, &errorCode));
+CollationLoader::~CollationLoader() {
+ ures_close(data);
+ ures_close(collations);
+ ures_close(bundle);
+}
+
+const CollationCacheEntry *
+CollationLoader::createCacheEntry(UErrorCode &errorCode) {
+ // This is a linear lookup and fallback flow turned into a state machine.
+ // Most local variables have been turned into instance fields.
+ // In a cache miss, cache.get() calls CacheKey::createObject(),
+ // which means that we progress via recursion.
+ // loadFromCollations() will recurse to itself as well for collation type fallback.
+ if(bundle == NULL) {
+ return loadFromLocale(errorCode);
+ } else if(collations == NULL) {
+ return loadFromBundle(errorCode);
+ } else if(data == NULL) {
+ return loadFromCollations(errorCode);
+ } else {
+ return loadFromData(errorCode);
+ }
+}
+
+const CollationCacheEntry *
+CollationLoader::loadFromLocale(UErrorCode &errorCode) {
+ if(U_FAILURE(errorCode)) { return NULL; }
+ U_ASSERT(bundle == NULL);
+ bundle = ures_open(U_ICUDATA_COLL, locale.getBaseName(), &errorCode);
if(errorCode == U_MISSING_RESOURCE_ERROR) {
errorCode = U_USING_DEFAULT_WARNING;
- validLocale = Locale::getRoot();
- return root;
+
+ // Have to add that ref that we promise.
+ rootEntry->addRef();
+ return rootEntry;
}
- const char *vLocale = ures_getLocaleByType(bundle.getAlias(), ULOC_ACTUAL_LOCALE, &errorCode);
+ Locale requestedLocale(locale);
+ const char *vLocale = ures_getLocaleByType(bundle, ULOC_ACTUAL_LOCALE, &errorCode);
if(U_FAILURE(errorCode)) { return NULL; }
- validLocale = Locale(vLocale);
+ locale = validLocale = Locale(vLocale); // no type until loadFromCollations()
+ if(type[0] != 0) {
+ locale.setKeywordValue("collation", type, errorCode);
+ }
+ if(locale != requestedLocale) {
+ return getCacheEntry(errorCode);
+ } else {
+ return loadFromBundle(errorCode);
+ }
+}
+const CollationCacheEntry *
+CollationLoader::loadFromBundle(UErrorCode &errorCode) {
+ if(U_FAILURE(errorCode)) { return NULL; }
+ U_ASSERT(collations == NULL);
// There are zero or more tailorings in the collations table.
- LocalUResourceBundlePointer collations(
- ures_getByKey(bundle.getAlias(), "collations", NULL, &errorCode));
+ collations = ures_getByKey(bundle, "collations", NULL, &errorCode);
if(errorCode == U_MISSING_RESOURCE_ERROR) {
errorCode = U_USING_DEFAULT_WARNING;
- return root;
+ // Return the root tailoring with the validLocale, without collation type.
+ return makeCacheEntryFromRoot(validLocale, errorCode);
}
if(U_FAILURE(errorCode)) { return NULL; }
- // Fetch the collation type from the locale ID and the default type from the data.
- char type[16];
- int32_t typeLength = locale.getKeywordValue("collation", type, LENGTHOF(type) - 1, errorCode);
- if(U_FAILURE(errorCode)) {
- errorCode = U_ILLEGAL_ARGUMENT_ERROR;
- return NULL;
- }
- type[typeLength] = 0; // in case of U_NOT_TERMINATED_WARNING
- char defaultType[16];
+ // Fetch the default type from the data.
{
UErrorCode internalErrorCode = U_ZERO_ERROR;
LocalUResourceBundlePointer def(
- ures_getByKeyWithFallback(collations.getAlias(), "default", NULL,
- &internalErrorCode));
+ ures_getByKeyWithFallback(collations, "default", NULL, &internalErrorCode));
int32_t length;
const UChar *s = ures_getString(def.getAlias(), &length, &internalErrorCode);
- if(U_SUCCESS(internalErrorCode) && length < LENGTHOF(defaultType)) {
+ if(U_SUCCESS(internalErrorCode) && 0 < length && length < LENGTHOF(defaultType)) {
u_UCharsToChars(s, defaultType, length + 1);
} else {
uprv_strcpy(defaultType, "standard");
}
}
- if(typeLength == 0 || uprv_strcmp(type, "default") == 0) {
+
+ // Record which collation types we have looked for already,
+ // so that we do not deadlock in the cache.
+ //
+ // If there is no explicit type, then we look in the cache
+ // for the entry with the default type.
+ // If the explicit type is the default type, then we do not look in the cache
+ // for the entry with an empty type.
+ // Otherwise, two concurrent requests with opposite fallbacks would deadlock each other.
+ // Also, it is easier to always enter the next method with a non-empty type.
+ if(type[0] == 0) {
uprv_strcpy(type, defaultType);
+ typesTried |= TRIED_DEFAULT;
+ if(uprv_strcmp(type, "search") == 0) {
+ typesTried |= TRIED_SEARCH;
+ }
+ if(uprv_strcmp(type, "standard") == 0) {
+ typesTried |= TRIED_STANDARD;
+ }
+ locale.setKeywordValue("collation", type, errorCode);
+ return getCacheEntry(errorCode);
} else {
- T_CString_toLowerCase(type);
+ if(uprv_strcmp(type, defaultType) == 0) {
+ typesTried |= TRIED_DEFAULT;
+ }
+ if(uprv_strcmp(type, "search") == 0) {
+ typesTried |= TRIED_SEARCH;
+ }
+ if(uprv_strcmp(type, "standard") == 0) {
+ typesTried |= TRIED_STANDARD;
+ }
+ return loadFromCollations(errorCode);
}
+}
+const CollationCacheEntry *
+CollationLoader::loadFromCollations(UErrorCode &errorCode) {
+ if(U_FAILURE(errorCode)) { return NULL; }
+ U_ASSERT(data == NULL);
// Load the collations/type tailoring, with type fallback.
- UBool typeFallback = FALSE;
- LocalUResourceBundlePointer data(
- ures_getByKeyWithFallback(collations.getAlias(), type, NULL, &errorCode));
- if(errorCode == U_MISSING_RESOURCE_ERROR &&
- typeLength > 6 && uprv_strncmp(type, "search", 6) == 0) {
- // fall back from something like "searchjl" to "search"
- typeFallback = TRUE;
- type[6] = 0;
- errorCode = U_ZERO_ERROR;
- data.adoptInstead(
- ures_getByKeyWithFallback(collations.getAlias(), type, NULL, &errorCode));
- }
- if(errorCode == U_MISSING_RESOURCE_ERROR && uprv_strcmp(type, defaultType) != 0) {
- // fall back to the default type
- typeFallback = TRUE;
- uprv_strcpy(type, defaultType);
- errorCode = U_ZERO_ERROR;
- data.adoptInstead(
- ures_getByKeyWithFallback(collations.getAlias(), type, NULL, &errorCode));
- }
- if(errorCode == U_MISSING_RESOURCE_ERROR && uprv_strcmp(type, "standard") != 0) {
- // fall back to the "standard" type
- typeFallback = TRUE;
- uprv_strcpy(type, "standard");
- errorCode = U_ZERO_ERROR;
- data.adoptInstead(
- ures_getByKeyWithFallback(collations.getAlias(), type, NULL, &errorCode));
- }
+ LocalUResourceBundlePointer localData(
+ ures_getByKeyWithFallback(collations, type, NULL, &errorCode));
+ int32_t typeLength = uprv_strlen(type);
if(errorCode == U_MISSING_RESOURCE_ERROR) {
errorCode = U_USING_DEFAULT_WARNING;
- return root;
+ typeFallback = TRUE;
+ if((typesTried & TRIED_SEARCH) == 0 &&
+ typeLength > 6 && uprv_strncmp(type, "search", 6) == 0) {
+ // fall back from something like "searchjl" to "search"
+ typesTried |= TRIED_SEARCH;
+ type[6] = 0;
+ } else if((typesTried & TRIED_DEFAULT) == 0) {
+ // fall back to the default type
+ typesTried |= TRIED_DEFAULT;
+ uprv_strcpy(type, defaultType);
+ } else if((typesTried & TRIED_STANDARD) == 0) {
+ // fall back to the "standard" type
+ typesTried |= TRIED_STANDARD;
+ uprv_strcpy(type, "standard");
+ } else {
+ // Return the root tailoring with the validLocale, without collation type.
+ return makeCacheEntryFromRoot(validLocale, errorCode);
+ }
+ locale.setKeywordValue("collation", type, errorCode);
+ return getCacheEntry(errorCode);
}
if(U_FAILURE(errorCode)) { return NULL; }
- LocalPointer<CollationTailoring> t(new CollationTailoring(root->settings));
- if(t.isNull() || t->isBogus()) {
- errorCode = U_MEMORY_ALLOCATION_ERROR;
- return NULL;
+ data = localData.orphan();
+ const char *actualLocale = ures_getLocaleByType(data, ULOC_ACTUAL_LOCALE, &errorCode);
+ if(U_FAILURE(errorCode)) { return NULL; }
+ const char *vLocale = validLocale.getBaseName();
+ UBool actualAndValidLocalesAreDifferent = uprv_strcmp(actualLocale, vLocale) != 0;
+
+ // Set the collation types on the informational locales,
+ // except when they match the default types (for brevity and backwards compatibility).
+ // For the valid locale, suppress the default type.
+ if(uprv_strcmp(type, defaultType) != 0) {
+ validLocale.setKeywordValue("collation", type, errorCode);
+ if(U_FAILURE(errorCode)) { return NULL; }
}
// Is this the same as the root collator? If so, then use that instead.
- const char *actualLocale = ures_getLocaleByType(data.getAlias(), ULOC_ACTUAL_LOCALE, &errorCode);
- if(U_FAILURE(errorCode)) { return NULL; }
if((*actualLocale == 0 || uprv_strcmp(actualLocale, "root") == 0) &&
uprv_strcmp(type, "standard") == 0) {
if(typeFallback) {
errorCode = U_USING_DEFAULT_WARNING;
}
- return root;
+ return makeCacheEntryFromRoot(validLocale, errorCode);
+ }
+
+ locale = Locale(actualLocale);
+ if(actualAndValidLocalesAreDifferent) {
+ locale.setKeywordValue("collation", type, errorCode);
+ const CollationCacheEntry *entry = getCacheEntry(errorCode);
+ return makeCacheEntry(validLocale, entry, errorCode);
+ } else {
+ return loadFromData(errorCode);
+ }
+}
+
+const CollationCacheEntry *
+CollationLoader::loadFromData(UErrorCode &errorCode) {
+ if(U_FAILURE(errorCode)) { return NULL; }
+ LocalPointer<CollationTailoring> t(new CollationTailoring(rootEntry->tailoring->settings));
+ if(t.isNull() || t->isBogus()) {
+ errorCode = U_MEMORY_ALLOCATION_ERROR;
+ return NULL;
}
- t->actualLocale = Locale(actualLocale);
// deserialize
- LocalUResourceBundlePointer binary(
- ures_getByKey(data.getAlias(), "%%CollationBin", NULL, &errorCode));
+ LocalUResourceBundlePointer binary(ures_getByKey(data, "%%CollationBin", NULL, &errorCode));
// Note: U_MISSING_RESOURCE_ERROR --> The old code built from rules if available
// but that created undesirable dependencies.
int32_t length;
const uint8_t *inBytes = ures_getBinary(binary.getAlias(), &length, &errorCode);
- if(U_FAILURE(errorCode)) { return NULL; }
- CollationDataReader::read(root, inBytes, length, *t, errorCode);
+ CollationDataReader::read(rootEntry->tailoring, inBytes, length, *t, errorCode);
// Note: U_COLLATOR_VERSION_MISMATCH --> The old code built from rules if available
// but that created undesirable dependencies.
if(U_FAILURE(errorCode)) { return NULL; }
{
UErrorCode internalErrorCode = U_ZERO_ERROR;
int32_t length;
- const UChar *s = ures_getStringByKey(data.getAlias(), "Sequence", &length,
+ const UChar *s = ures_getStringByKey(data, "Sequence", &length,
&internalErrorCode);
- if(U_SUCCESS(errorCode)) {
+ if(U_SUCCESS(internalErrorCode)) {
t->rules.setTo(TRUE, s, length);
}
}
- // Set the collation types on the informational locales,
- // except when they match the default types (for brevity and backwards compatibility).
- // For the valid locale, suppress the default type.
- if(uprv_strcmp(type, defaultType) != 0) {
- validLocale.setKeywordValue("collation", type, errorCode);
- if(U_FAILURE(errorCode)) { return NULL; }
- }
+ const char *actualLocale = locale.getBaseName(); // without type
+ const char *vLocale = validLocale.getBaseName();
+ UBool actualAndValidLocalesAreDifferent = uprv_strcmp(actualLocale, vLocale) != 0;
// For the actual locale, suppress the default type *according to the actual locale*.
// For example, zh has default=pinyin and contains all of the Chinese tailorings.
// zh_Hant has default=stroke but has no other data.
// For the valid locale "zh_Hant" we need to suppress stroke.
// For the actual locale "zh" we need to suppress pinyin instead.
- if(uprv_strcmp(actualLocale, vLocale) != 0) {
+ if(actualAndValidLocalesAreDifferent) {
// Opening a bundle for the actual locale should always succeed.
LocalUResourceBundlePointer actualBundle(
ures_open(U_ICUDATA_COLL, actualLocale, &errorCode));
uprv_strcpy(defaultType, "standard");
}
}
+ t->actualLocale = locale;
if(uprv_strcmp(type, defaultType) != 0) {
t->actualLocale.setKeywordValue("collation", type, errorCode);
- if(U_FAILURE(errorCode)) { return NULL; }
+ } else if(uprv_strcmp(locale.getName(), locale.getBaseName()) != 0) {
+ // Remove the collation keyword if it was set.
+ t->actualLocale.setKeywordValue("collation", NULL, errorCode);
}
+ if(U_FAILURE(errorCode)) { return NULL; }
if(typeFallback) {
errorCode = U_USING_DEFAULT_WARNING;
}
- t->bundle = bundle.orphan();
- return t.orphan();
+ t->bundle = bundle;
+ bundle = NULL;
+ const CollationCacheEntry *entry = new CollationCacheEntry(validLocale, t.getAlias());
+ if(entry == NULL) {
+ errorCode = U_MEMORY_ALLOCATION_ERROR;
+ } else {
+ t.orphan();
+ }
+ // Have to add that reference that we promise.
+ entry->addRef();
+ return entry;
+}
+
+const CollationCacheEntry *
+CollationLoader::getCacheEntry(UErrorCode &errorCode) {
+ LocaleCacheKey<CollationCacheEntry> key(locale);
+ const CollationCacheEntry *entry = NULL;
+ cache->get(key, this, entry, errorCode);
+ return entry;
+}
+
+const CollationCacheEntry *
+CollationLoader::makeCacheEntryFromRoot(
+ const Locale &loc,
+ UErrorCode &errorCode) const {
+ if (U_FAILURE(errorCode)) {
+ return NULL;
+ }
+ rootEntry->addRef();
+ return makeCacheEntry(validLocale, rootEntry, errorCode);
+}
+
+const CollationCacheEntry *
+CollationLoader::makeCacheEntry(
+ const Locale &loc,
+ const CollationCacheEntry *entryFromCache,
+ UErrorCode &errorCode) {
+ if(U_FAILURE(errorCode) || loc == entryFromCache->validLocale) {
+ return entryFromCache;
+ }
+ CollationCacheEntry *entry = new CollationCacheEntry(loc, entryFromCache->tailoring);
+ if(entry == NULL) {
+ errorCode = U_MEMORY_ALLOCATION_ERROR;
+ entryFromCache->removeRef();
+ return NULL;
+ }
+ entry->addRef();
+ entryFromCache->removeRef();
+ return entry;
}
U_NAMESPACE_END
U_NAMESPACE_BEGIN
+struct CollationCacheEntry;
struct CollationData;
struct CollationSettings;
struct CollationTailoring;
friend class CollationElementIterator;
friend class Collator;
- RuleBasedCollator(const CollationTailoring *t, const Locale &vl);
+ RuleBasedCollator(const CollationCacheEntry *entry);
/**
* Enumeration of attributes that are relevant for short definition strings
ATTR_LIMIT
};
- void adoptTailoring(CollationTailoring *t);
+ void adoptTailoring(CollationTailoring *t, UErrorCode &errorCode);
// Both lengths must be <0 or else both must be >=0.
UCollationResult doCompare(const UChar *left, int32_t leftLength,
const CollationData *data;
const CollationSettings *settings; // reference-counted
- const CollationTailoring *tailoring; // reference-counted
+ const CollationTailoring *tailoring; // alias of cacheEntry->tailoring
+ const CollationCacheEntry *cacheEntry; // reference-counted
Locale validLocale;
uint32_t explicitlySetAttributes;
incaltst.o calcasts.o v32test.o uvectest.o textfile.o tokiter.o utxttest.o \
windttst.o winnmtst.o winutil.o csdetest.o tzrulets.o tzoffloc.o tzfmttst.o ssearch.o dtifmtts.o \
tufmtts.o itspoof.o simplethread.o bidiconf.o locnmtst.o dcfmtest.o alphaindextst.o listformattertest.o genderinfotest.o compactdecimalformattest.o regiontst.o \
-reldatefmttest.o lrucachetest.o simplepatternformattertest.o measfmttest.o scientificformathelpertest.o numfmtspectest.o
+reldatefmttest.o lrucachetest.o simplepatternformattertest.o measfmttest.o scientificformathelpertest.o numfmtspectest.o unifiedcachetest.o
DEPS = $(OBJECTS:.o=.d)
<ClCompile Include="tztest.cpp">\r
<DisableLanguageExtensions>false</DisableLanguageExtensions>\r
</ClCompile>\r
+ <ClCompile Include="unifiedcachetest.cpp">\r
+ <DisableLanguageExtensions>false</DisableLanguageExtensions>\r
+ </ClCompile>\r
<ClCompile Include="windttst.cpp">\r
<DisableLanguageExtensions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</DisableLanguageExtensions>\r
<DisableLanguageExtensions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</DisableLanguageExtensions>\r
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />\r
<ImportGroup Label="ExtensionTargets">\r
</ImportGroup>\r
-</Project>
\ No newline at end of file
+</Project>\r
<ClCompile Include="lrucachetest.cpp">\r
<Filter>collections</Filter>\r
</ClCompile>\r
+ <ClCompile Include="unifiedcachetest.cpp">\r
+ <Filter>collections</Filter>\r
+ </ClCompile>\r
<ClCompile Include="uvectest.cpp">\r
<Filter>collections</Filter>\r
</ClCompile>\r
<Filter>formatting</Filter>\r
</ClInclude>\r
</ItemGroup>\r
-</Project>
\ No newline at end of file
+</Project>\r
static IntlTest *createEnumSetTest();
extern IntlTest *createLRUCacheTest();
extern IntlTest *createSimplePatternFormatterTest();
+extern IntlTest *createUnifiedCacheTest();
#define CASE(id, test) case id: \
name = #test; \
callTest(*test, par);
}
break;
+ case 22:
+ name = "UnifiedCacheTest";
+ if (exec) {
+ logln("TestSuite UnifiedCacheTest---"); logln();
+ LocalPointer<IntlTest> test(createUnifiedCacheTest());
+ callTest(*test, par);
+ }
+ break;
default: name = ""; break; //needed to end loop
}
}
#include "unicode/measure.h"
#include "unicode/measunit.h"
#include "unicode/tmunit.h"
+#include "unicode/plurrule.h"
#include "charstr.h"
+#include "unicode/reldatefmt.h"
#define LENGTHOF(array) (int32_t)(sizeof(array) / sizeof((array)[0]))
#include "tsmthred.h"
#include "unicode/ushape.h"
#include "unicode/translit.h"
+#include "sharedobject.h"
+#include "unifiedcache.h"
+#include "uassert.h"
#if U_PLATFORM_USES_ONLY_WIN32_API
/* Prefer native Windows APIs even if POSIX is implemented (i.e., on Cygwin). */
TestConditionVariables();
}
break;
+ case 8:
+ name = "TestUnifiedCache";
+ if (exec) {
+ TestUnifiedCache();
+ }
+ break;
default:
name = "";
break; //needed to end loop
delete threads[i];
}
}
-
+
+static const char *gCacheLocales[] = {"en_US", "en_GB", "fr_FR", "fr"};
+static int32_t gObjectsCreated = 0;
+static const int32_t CACHE_LOAD = 3;
+
+class UCTMultiThreadItem : public SharedObject {
+ public:
+ char *value;
+ UCTMultiThreadItem(const char *x) : value(NULL) {
+ value = uprv_strdup(x);
+ }
+ virtual ~UCTMultiThreadItem() {
+ uprv_free(value);
+ }
+};
+
+template<> U_EXPORT
+const UCTMultiThreadItem *LocaleCacheKey<UCTMultiThreadItem>::createObject(
+ const void * /*unused*/, UErrorCode & /* status */) const {
+ // Since multiple threads are hitting the cache for the first time,
+ // no objects should be created yet.
+ umtx_lock(&gCTMutex);
+ if (gObjectsCreated != 0) {
+ gThisTest->errln("Expected no objects to be created yet.");
+ }
+ umtx_unlock(&gCTMutex);
+
+ // Big, expensive object that takes 1 second to create.
+ SimpleThread::sleep(1000);
+
+ // Log that we created an object.
+ umtx_lock(&gCTMutex);
+ ++gObjectsCreated;
+ umtx_unlock(&gCTMutex);
+ UCTMultiThreadItem *result = new UCTMultiThreadItem(fLoc.getName());
+ result->addRef();
+ return result;
+}
+
+class UnifiedCacheThread: public SimpleThread {
+ public:
+ UnifiedCacheThread(const char *loc) : fLoc(loc) {};
+ ~UnifiedCacheThread() {};
+ void run();
+ const char *fLoc;
+};
+
+void UnifiedCacheThread::run() {
+ UErrorCode status = U_ZERO_ERROR;
+ const UnifiedCache *cache = UnifiedCache::getInstance(status);
+ U_ASSERT(status == U_ZERO_ERROR);
+ const UCTMultiThreadItem *item = NULL;
+ cache->get(LocaleCacheKey<UCTMultiThreadItem>(fLoc), item, status);
+ U_ASSERT(item != NULL);
+ if (uprv_strcmp(fLoc, item->value)) {
+ gThisTest->errln("Expected %s, got %s", fLoc, item->value);
+ }
+ item->removeRef();
+
+ // Mark this thread as finished
+ umtx_lock(&gCTMutex);
+ ++gFinishedThreads;
+ umtx_condBroadcast(&gCTConditionVar);
+ umtx_unlock(&gCTMutex);
+}
+
+void MultithreadTest::TestUnifiedCache() {
+ UErrorCode status = U_ZERO_ERROR;
+ const UnifiedCache *cache = UnifiedCache::getInstance(status);
+ U_ASSERT(cache != NULL);
+ cache->flush();
+ gThisTest = this;
+ gFinishedThreads = 0;
+ gObjectsCreated = 0;
+
+ UnifiedCacheThread *threads[CACHE_LOAD][LENGTHOF(gCacheLocales)];
+ for (int32_t i=0; i<CACHE_LOAD; ++i) {
+ for (int32_t j=0; j<LENGTHOF(gCacheLocales); ++j) {
+ threads[i][j] = new UnifiedCacheThread(gCacheLocales[j]);
+ threads[i][j]->start();
+ }
+ }
+ // Wait on all the threads to complete verify that LENGTHOF(gCacheLocales)
+ // objects were created.
+ umtx_lock(&gCTMutex);
+ while (gFinishedThreads < CACHE_LOAD*LENGTHOF(gCacheLocales)) {
+ umtx_condWait(&gCTConditionVar, &gCTMutex);
+ }
+ assertEquals("Objects created", LENGTHOF(gCacheLocales), gObjectsCreated);
+ umtx_unlock(&gCTMutex);
+
+ // clean up threads
+ for (int32_t i=0; i<CACHE_LOAD; ++i) {
+ for (int32_t j=0; j<LENGTHOF(gCacheLocales); ++j) {
+ delete threads[i][j];
+ }
+ }
+}
+
#endif // ICU_USE_THREADS
void TestString();
void TestAnyTranslit();
void TestConditionVariables();
+ void TestUnifiedCache();
};
--- /dev/null
+/*
+*******************************************************************************
+* Copyright (C) 2014, International Business Machines Corporation and *
+* others. All Rights Reserved. *
+*******************************************************************************
+*
+* File UNIFIEDCACHETEST.CPP
+*
+********************************************************************************
+*/
+#include "cstring.h"
+#include "intltest.h"
+#include "unifiedcache.h"
+
+class UCTItem : public SharedObject {
+ public:
+ char *value;
+ UCTItem(const char *x) : value(NULL) {
+ value = uprv_strdup(x);
+ }
+ virtual ~UCTItem() {
+ uprv_free(value);
+ }
+};
+
+class UCTItem2 : public SharedObject {
+};
+
+template<> U_EXPORT
+const UCTItem *LocaleCacheKey<UCTItem>::createObject(
+ const void * /*unused*/, UErrorCode &status) const {
+ if (uprv_strcmp(fLoc.getName(), "zh") == 0) {
+ status = U_MISSING_RESOURCE_ERROR;
+ return NULL;
+ }
+ if (uprv_strcmp(fLoc.getLanguage(), fLoc.getName()) != 0) {
+ const UCTItem *item = NULL;
+ UnifiedCache::getByLocale(fLoc.getLanguage(), item, status);
+ if (U_FAILURE(status)) {
+ return NULL;
+ }
+ return item;
+ }
+ UCTItem *result = new UCTItem(fLoc.getName());
+ result->addRef();
+ return result;
+}
+
+template<> U_EXPORT
+const UCTItem2 *LocaleCacheKey<UCTItem2>::createObject(
+ const void * /*unused*/, UErrorCode & /*status*/) const {
+ return NULL;
+}
+
+class UnifiedCacheTest : public IntlTest {
+public:
+ UnifiedCacheTest() {
+ }
+ void runIndexedTest(int32_t index, UBool exec, const char *&name, char *par=0);
+private:
+ void TestBasic();
+ void TestError();
+ void TestHashEquals();
+};
+
+void UnifiedCacheTest::runIndexedTest(int32_t index, UBool exec, const char* &name, char* /*par*/) {
+ TESTCASE_AUTO_BEGIN;
+ TESTCASE_AUTO(TestBasic);
+ TESTCASE_AUTO(TestError);
+ TESTCASE_AUTO(TestHashEquals);
+ TESTCASE_AUTO_END;
+}
+
+void UnifiedCacheTest::TestBasic() {
+ UErrorCode status = U_ZERO_ERROR;
+ const UnifiedCache *cache = UnifiedCache::getInstance(status);
+ assertSuccess("", status);
+ cache->flush();
+ int32_t baseCount = cache->keyCount();
+ const UCTItem *en = NULL;
+ const UCTItem *enGb = NULL;
+ const UCTItem *enGb2 = NULL;
+ const UCTItem *enUs = NULL;
+ const UCTItem *fr = NULL;
+ const UCTItem *frFr = NULL;
+ cache->get(LocaleCacheKey<UCTItem>("en"), en, status);
+ cache->get(LocaleCacheKey<UCTItem>("en_US"), enUs, status);
+ cache->get(LocaleCacheKey<UCTItem>("en_GB"), enGb, status);
+ cache->get(LocaleCacheKey<UCTItem>("fr_FR"), frFr, status);
+ cache->get(LocaleCacheKey<UCTItem>("fr"), fr, status);
+ cache->get(LocaleCacheKey<UCTItem>("en_GB"), enGb2, status);
+ SharedObject::clearPtr(enGb2);
+ if (enGb != enUs) {
+ errln("Expected en_GB and en_US to resolve to same object.");
+ }
+ if (fr != frFr) {
+ errln("Expected fr and fr_FR to resolve to same object.");
+ }
+ if (enGb == fr) {
+ errln("Expected en_GB and fr to return different objects.");
+ }
+ assertSuccess("", status);
+ // en_US, en_GB, en share one object; fr_FR and fr don't share.
+ // 5 keys in all.
+ assertEquals("", baseCount + 5, cache->keyCount());
+ SharedObject::clearPtr(enGb);
+ cache->flush();
+ assertEquals("", baseCount + 5, cache->keyCount());
+ SharedObject::clearPtr(enUs);
+ SharedObject::clearPtr(en);
+ cache->flush();
+ // With en_GB and en_US and en cleared there are no more hard references to
+ // the "en" object, so it gets flushed and the keys that refer to it
+ // get removed from the cache.
+ assertEquals("", baseCount + 2, cache->keyCount());
+ SharedObject::clearPtr(fr);
+ cache->flush();
+ assertEquals("", baseCount + 2, cache->keyCount());
+ SharedObject::clearPtr(frFr);
+ cache->flush();
+ assertEquals("", baseCount + 0, cache->keyCount());
+}
+
+void UnifiedCacheTest::TestError() {
+ UErrorCode status = U_ZERO_ERROR;
+ const UnifiedCache *cache = UnifiedCache::getInstance(status);
+ assertSuccess("", status);
+ cache->flush();
+ int32_t baseCount = cache->keyCount();
+ const UCTItem *zh = NULL;
+ const UCTItem *zhTw = NULL;
+ const UCTItem *zhHk = NULL;
+
+ status = U_ZERO_ERROR;
+ cache->get(LocaleCacheKey<UCTItem>("zh"), zh, status);
+ if (status != U_MISSING_RESOURCE_ERROR) {
+ errln("Expected U_MISSING_RESOURCE_ERROR");
+ }
+ status = U_ZERO_ERROR;
+ cache->get(LocaleCacheKey<UCTItem>("zh_TW"), zhTw, status);
+ if (status != U_MISSING_RESOURCE_ERROR) {
+ errln("Expected U_MISSING_RESOURCE_ERROR");
+ }
+ status = U_ZERO_ERROR;
+ cache->get(LocaleCacheKey<UCTItem>("zh_HK"), zhHk, status);
+ if (status != U_MISSING_RESOURCE_ERROR) {
+ errln("Expected U_MISSING_RESOURCE_ERROR");
+ }
+ // 3 keys in cache zh, zhTW, zhHk all pointing to error placeholders
+ assertEquals("", baseCount + 3, cache->keyCount());
+ cache->flush();
+ // error placeholders have no hard references so they always get flushed.
+ assertEquals("", baseCount + 0, cache->keyCount());
+}
+
+void UnifiedCacheTest::TestHashEquals() {
+ LocaleCacheKey<UCTItem> key1("en_US");
+ LocaleCacheKey<UCTItem> key2("en_US");
+ LocaleCacheKey<UCTItem> diffKey1("en_UT");
+ LocaleCacheKey<UCTItem2> diffKey2("en_US");
+ assertTrue("", key1.hashCode() == key2.hashCode());
+ assertTrue("", key1.hashCode() != diffKey1.hashCode());
+ assertTrue("", key1.hashCode() != diffKey2.hashCode());
+ assertTrue("", diffKey1.hashCode() != diffKey2.hashCode());
+ assertTrue("", key1 == key2);
+ assertTrue("", key1 != diffKey1);
+ assertTrue("", key1 != diffKey2);
+ assertTrue("", diffKey1 != diffKey2);
+}
+
+extern IntlTest *createUnifiedCacheTest() {
+ return new UnifiedCacheTest();
+}