mirror of
https://github.com/unicode-org/icu.git
synced 2025-04-07 22:44:49 +00:00
ICU-11908 NumberingSystem, fix the memory management of static cache of numsys names.
Add thread safe cache initialization.
This commit is contained in:
parent
3a28fb7216
commit
f7b36d39bc
5 changed files with 110 additions and 95 deletions
|
@ -26,6 +26,8 @@
|
|||
#include "unicode/numsys.h"
|
||||
#include "cstring.h"
|
||||
#include "uassert.h"
|
||||
#include "ucln_in.h"
|
||||
#include "umutex.h"
|
||||
#include "uresimp.h"
|
||||
#include "numsys_impl.h"
|
||||
|
||||
|
@ -266,75 +268,82 @@ UBool NumberingSystem::isAlgorithmic() const {
|
|||
return ( algorithmic );
|
||||
}
|
||||
|
||||
StringEnumeration* NumberingSystem::getAvailableNames(UErrorCode &status) {
|
||||
// TODO(ticket #11908): Init-once static cache, with u_cleanup() callback.
|
||||
static StringEnumeration* availableNames = nullptr;
|
||||
namespace {
|
||||
|
||||
if (U_FAILURE(status)) {
|
||||
return nullptr;
|
||||
}
|
||||
UVector* gNumsysNames = nullptr;
|
||||
UInitOnce gNumSysInitOnce = U_INITONCE_INITIALIZER;
|
||||
|
||||
if ( availableNames == nullptr ) {
|
||||
// TODO: Simple array of UnicodeString objects, based on length of table resource?
|
||||
LocalPointer<UVector> numsysNames(new UVector(uprv_deleteUObject, nullptr, status), status);
|
||||
if (U_FAILURE(status)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
UErrorCode rbstatus = U_ZERO_ERROR;
|
||||
UResourceBundle *numberingSystemsInfo = ures_openDirect(nullptr, "numberingSystems", &rbstatus);
|
||||
numberingSystemsInfo = ures_getByKey(numberingSystemsInfo, "numberingSystems", numberingSystemsInfo, &rbstatus);
|
||||
if (U_FAILURE(rbstatus)) {
|
||||
// Don't stomp on the catastrophic failure of OOM.
|
||||
if (rbstatus == U_MEMORY_ALLOCATION_ERROR) {
|
||||
status = rbstatus;
|
||||
} else {
|
||||
status = U_MISSING_RESOURCE_ERROR;
|
||||
}
|
||||
ures_close(numberingSystemsInfo);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
while ( ures_hasNext(numberingSystemsInfo) && U_SUCCESS(status) ) {
|
||||
LocalUResourceBundlePointer nsCurrent(ures_getNextResource(numberingSystemsInfo, nullptr, &rbstatus));
|
||||
if (rbstatus == U_MEMORY_ALLOCATION_ERROR) {
|
||||
status = rbstatus; // we want to report OOM failure back to the caller.
|
||||
break;
|
||||
}
|
||||
const char *nsName = ures_getKey(nsCurrent.getAlias());
|
||||
LocalPointer<UnicodeString> newElem(new UnicodeString(nsName, -1, US_INV), status);
|
||||
if (U_SUCCESS(status)) {
|
||||
numsysNames->addElement(newElem.getAlias(), status);
|
||||
if (U_SUCCESS(status)) {
|
||||
newElem.orphan(); // on success, the numsysNames vector owns newElem.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ures_close(numberingSystemsInfo);
|
||||
if (U_FAILURE(status)) {
|
||||
return nullptr;
|
||||
}
|
||||
availableNames = new NumsysNameEnumeration(numsysNames.getAlias(), status);
|
||||
if (availableNames == nullptr) {
|
||||
status = U_MEMORY_ALLOCATION_ERROR;
|
||||
return nullptr;
|
||||
}
|
||||
numsysNames.orphan(); // The names got adopted.
|
||||
}
|
||||
|
||||
return availableNames;
|
||||
U_CFUNC UBool U_CALLCONV numSysCleanup() {
|
||||
delete gNumsysNames;
|
||||
gNumsysNames = nullptr;
|
||||
gNumSysInitOnce.reset();
|
||||
return true;
|
||||
}
|
||||
|
||||
NumsysNameEnumeration::NumsysNameEnumeration(UVector *numsysNames, UErrorCode& /*status*/) {
|
||||
pos=0;
|
||||
fNumsysNames = numsysNames;
|
||||
U_CFUNC void initNumsysNames(UErrorCode &status) {
|
||||
U_ASSERT(gNumsysNames == nullptr);
|
||||
ucln_i18n_registerCleanup(UCLN_I18N_NUMSYS, numSysCleanup);
|
||||
|
||||
// TODO: Simple array of UnicodeString objects, based on length of table resource?
|
||||
LocalPointer<UVector> numsysNames(new UVector(uprv_deleteUObject, nullptr, status), status);
|
||||
if (U_FAILURE(status)) {
|
||||
return;
|
||||
}
|
||||
|
||||
UErrorCode rbstatus = U_ZERO_ERROR;
|
||||
UResourceBundle *numberingSystemsInfo = ures_openDirect(nullptr, "numberingSystems", &rbstatus);
|
||||
numberingSystemsInfo =
|
||||
ures_getByKey(numberingSystemsInfo, "numberingSystems", numberingSystemsInfo, &rbstatus);
|
||||
if (U_FAILURE(rbstatus)) {
|
||||
// Don't stomp on the catastrophic failure of OOM.
|
||||
if (rbstatus == U_MEMORY_ALLOCATION_ERROR) {
|
||||
status = rbstatus;
|
||||
} else {
|
||||
status = U_MISSING_RESOURCE_ERROR;
|
||||
}
|
||||
ures_close(numberingSystemsInfo);
|
||||
return;
|
||||
}
|
||||
|
||||
while ( ures_hasNext(numberingSystemsInfo) && U_SUCCESS(status) ) {
|
||||
LocalUResourceBundlePointer nsCurrent(ures_getNextResource(numberingSystemsInfo, nullptr, &rbstatus));
|
||||
if (rbstatus == U_MEMORY_ALLOCATION_ERROR) {
|
||||
status = rbstatus; // we want to report OOM failure back to the caller.
|
||||
break;
|
||||
}
|
||||
const char *nsName = ures_getKey(nsCurrent.getAlias());
|
||||
LocalPointer<UnicodeString> newElem(new UnicodeString(nsName, -1, US_INV), status);
|
||||
if (U_SUCCESS(status)) {
|
||||
numsysNames->addElement(newElem.getAlias(), status);
|
||||
if (U_SUCCESS(status)) {
|
||||
newElem.orphan(); // on success, the numsysNames vector owns newElem.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ures_close(numberingSystemsInfo);
|
||||
if (U_SUCCESS(status)) {
|
||||
gNumsysNames = numsysNames.orphan();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
} // end anonymous namespace
|
||||
|
||||
StringEnumeration* NumberingSystem::getAvailableNames(UErrorCode &status) {
|
||||
umtx_initOnce(gNumSysInitOnce, &initNumsysNames, status);
|
||||
LocalPointer<StringEnumeration> result(new NumsysNameEnumeration(status), status);
|
||||
return result.orphan();
|
||||
}
|
||||
|
||||
NumsysNameEnumeration::NumsysNameEnumeration(UErrorCode& status) : pos(0) {
|
||||
(void)status;
|
||||
}
|
||||
|
||||
const UnicodeString*
|
||||
NumsysNameEnumeration::snext(UErrorCode& status) {
|
||||
if (U_SUCCESS(status) && (fNumsysNames != nullptr) && (pos < fNumsysNames->size())) {
|
||||
return (const UnicodeString*)fNumsysNames->elementAt(pos++);
|
||||
if (U_SUCCESS(status) && (gNumsysNames != nullptr) && (pos < gNumsysNames->size())) {
|
||||
return (const UnicodeString*)gNumsysNames->elementAt(pos++);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -346,11 +355,10 @@ NumsysNameEnumeration::reset(UErrorCode& /*status*/) {
|
|||
|
||||
int32_t
|
||||
NumsysNameEnumeration::count(UErrorCode& /*status*/) const {
|
||||
return (fNumsysNames==nullptr) ? 0 : fNumsysNames->size();
|
||||
return (gNumsysNames==nullptr) ? 0 : gNumsysNames->size();
|
||||
}
|
||||
|
||||
NumsysNameEnumeration::~NumsysNameEnumeration() {
|
||||
delete fNumsysNames;
|
||||
}
|
||||
U_NAMESPACE_END
|
||||
|
||||
|
|
|
@ -26,18 +26,16 @@ U_NAMESPACE_BEGIN
|
|||
|
||||
class NumsysNameEnumeration : public StringEnumeration {
|
||||
public:
|
||||
// NumsysNameEnumeration instance adopts numsysNames
|
||||
NumsysNameEnumeration(UVector *numsysNames, UErrorCode& status);
|
||||
NumsysNameEnumeration(UErrorCode& status);
|
||||
|
||||
virtual ~NumsysNameEnumeration();
|
||||
static UClassID U_EXPORT2 getStaticClassID(void);
|
||||
virtual UClassID getDynamicClassID(void) const;
|
||||
virtual const UnicodeString* snext(UErrorCode& status);
|
||||
virtual void reset(UErrorCode& status);
|
||||
virtual int32_t count(UErrorCode& status) const;
|
||||
virtual UClassID getDynamicClassID(void) const override;
|
||||
virtual const UnicodeString* snext(UErrorCode& status) override;
|
||||
virtual void reset(UErrorCode& status) override;
|
||||
virtual int32_t count(UErrorCode& status) const override;
|
||||
private:
|
||||
int32_t pos;
|
||||
UVector *fNumsysNames = nullptr;
|
||||
};
|
||||
|
||||
U_NAMESPACE_END
|
||||
|
|
|
@ -60,6 +60,7 @@ typedef enum ECleanupI18NType {
|
|||
UCLN_I18N_CDFINFO,
|
||||
UCLN_I18N_REGION,
|
||||
UCLN_I18N_LIST_FORMATTER,
|
||||
UCLN_I18N_NUMSYS,
|
||||
UCLN_I18N_COUNT /* This must be last */
|
||||
} ECleanupI18NType;
|
||||
|
||||
|
|
|
@ -109,6 +109,10 @@ public:
|
|||
/**
|
||||
* Return a StringEnumeration over all the names of numbering systems known to ICU.
|
||||
* The numbering system names will be in alphabetical (invariant) order.
|
||||
*
|
||||
* The returned StringEnumeration is owned by the caller, who must delete it when
|
||||
* finished with it.
|
||||
*
|
||||
* @stable ICU 4.2
|
||||
*/
|
||||
static StringEnumeration * U_EXPORT2 getAvailableNames(UErrorCode& status);
|
||||
|
|
|
@ -2500,33 +2500,37 @@ static void TestUNumberingSystem(void) {
|
|||
}
|
||||
}
|
||||
|
||||
status = U_ZERO_ERROR;
|
||||
uenum = unumsys_openAvailableNames(&status);
|
||||
if ( U_SUCCESS(status) ) {
|
||||
int32_t numsysCount = 0;
|
||||
// sanity check for a couple of number systems that must be in the enumeration
|
||||
UBool foundLatn = FALSE;
|
||||
UBool foundArab = FALSE;
|
||||
while ( (numsys = uenum_next(uenum, NULL, &status)) != NULL && U_SUCCESS(status) ) {
|
||||
status = U_ZERO_ERROR;
|
||||
unumsys = unumsys_openByName(numsys, &status);
|
||||
if ( U_SUCCESS(status) ) {
|
||||
numsysCount++;
|
||||
if ( uprv_strcmp(numsys, "latn") ) foundLatn = TRUE;
|
||||
if ( uprv_strcmp(numsys, "arab") ) foundArab = TRUE;
|
||||
unumsys_close(unumsys);
|
||||
} else {
|
||||
log_err("unumsys_openAvailableNames includes %s but unumsys_openByName on it fails with status %s\n",
|
||||
numsys, myErrorName(status));
|
||||
for (int i=0; i<3; ++i) {
|
||||
// Run the test of unumsys_openAvailableNames() multiple times.
|
||||
// Helps verify the management of the internal cache of the names.
|
||||
status = U_ZERO_ERROR;
|
||||
uenum = unumsys_openAvailableNames(&status);
|
||||
if ( U_SUCCESS(status) ) {
|
||||
int32_t numsysCount = 0;
|
||||
// sanity check for a couple of number systems that must be in the enumeration
|
||||
UBool foundLatn = FALSE;
|
||||
UBool foundArab = FALSE;
|
||||
while ( (numsys = uenum_next(uenum, NULL, &status)) != NULL && U_SUCCESS(status) ) {
|
||||
status = U_ZERO_ERROR;
|
||||
unumsys = unumsys_openByName(numsys, &status);
|
||||
if ( U_SUCCESS(status) ) {
|
||||
numsysCount++;
|
||||
if ( uprv_strcmp(numsys, "latn") ) foundLatn = TRUE;
|
||||
if ( uprv_strcmp(numsys, "arab") ) foundArab = TRUE;
|
||||
unumsys_close(unumsys);
|
||||
} else {
|
||||
log_err("unumsys_openAvailableNames includes %s but unumsys_openByName on it fails with status %s\n",
|
||||
numsys, myErrorName(status));
|
||||
}
|
||||
}
|
||||
uenum_close(uenum);
|
||||
if ( numsysCount < 40 || !foundLatn || !foundArab ) {
|
||||
log_err("unumsys_openAvailableNames results incomplete: numsysCount %d, foundLatn %d, foundArab %d\n",
|
||||
numsysCount, foundLatn, foundArab);
|
||||
}
|
||||
} else {
|
||||
log_data_err("unumsys_openAvailableNames fails with status %s\n", myErrorName(status));
|
||||
}
|
||||
uenum_close(uenum);
|
||||
if ( numsysCount < 40 || !foundLatn || !foundArab ) {
|
||||
log_err("unumsys_openAvailableNames results incomplete: numsysCount %d, foundLatn %d, foundArab %d\n",
|
||||
numsysCount, foundLatn, foundArab);
|
||||
}
|
||||
} else {
|
||||
log_data_err("unumsys_openAvailableNames fails with status %s\n", myErrorName(status));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue