ICU-20104 Fix lazy init in Indian Calendar. Now matches other calendars. (#108)

* ICU-20104 Fix lazy init in Indian Calendar. Now matches other calendars.

* ICU-20104 export class IndianCalendar for testing.

* ICU-20104 shot-in-the-dark Windows build experiment.

* ICU-20104 fix memory leak in added test.
This commit is contained in:
Andy Heninger 2018-09-18 13:04:15 -07:00 committed by Shane Carr
parent f165bf10e7
commit f90676c2bc
No known key found for this signature in database
GPG key ID: FCED3B24AAB18B5C
4 changed files with 75 additions and 123 deletions

View file

@ -347,12 +347,15 @@ IndianCalendar::inDaylightTime(UErrorCode& status) const
return (UBool)(U_SUCCESS(status) ? (internalGet(UCAL_DST_OFFSET) != 0) : FALSE);
}
// default century
const UDate IndianCalendar::fgSystemDefaultCentury = DBL_MIN;
const int32_t IndianCalendar::fgSystemDefaultCenturyYear = -1;
UDate IndianCalendar::fgSystemDefaultCenturyStart = DBL_MIN;
int32_t IndianCalendar::fgSystemDefaultCenturyStartYear = -1;
/**
* The system maintains a static default century start date and Year. They are
* initialized the first time they are used. Once the system default century date
* and year are set, they do not change.
*/
static UDate gSystemDefaultCenturyStart = DBL_MIN;
static int32_t gSystemDefaultCenturyStartYear = -1;
static icu::UInitOnce gSystemDefaultCenturyInit = U_INITONCE_INITIALIZER;
UBool IndianCalendar::haveDefaultCentury() const
@ -360,88 +363,46 @@ UBool IndianCalendar::haveDefaultCentury() const
return TRUE;
}
UDate IndianCalendar::defaultCenturyStart() const
{
return internalGetDefaultCenturyStart();
}
int32_t IndianCalendar::defaultCenturyStartYear() const
{
return internalGetDefaultCenturyStartYear();
}
UDate
IndianCalendar::internalGetDefaultCenturyStart() const
{
// lazy-evaluate systemDefaultCenturyStart
UBool needsUpdate;
{
Mutex m;
needsUpdate = (fgSystemDefaultCenturyStart == fgSystemDefaultCentury);
}
if (needsUpdate) {
initializeSystemDefaultCentury();
}
// use defaultCenturyStart unless it's the flag value;
// then use systemDefaultCenturyStart
return fgSystemDefaultCenturyStart;
}
int32_t
IndianCalendar::internalGetDefaultCenturyStartYear() const
{
// lazy-evaluate systemDefaultCenturyStartYear
UBool needsUpdate;
{
Mutex m;
needsUpdate = (fgSystemDefaultCenturyStart == fgSystemDefaultCentury);
}
if (needsUpdate) {
initializeSystemDefaultCentury();
}
// use defaultCenturyStart unless it's the flag value;
// then use systemDefaultCenturyStartYear
return fgSystemDefaultCenturyStartYear;
}
void
IndianCalendar::initializeSystemDefaultCentury()
static void U_CALLCONV
initializeSystemDefaultCentury()
{
// initialize systemDefaultCentury and systemDefaultCenturyYear based
// on the current time. They'll be set to 80 years before
// the current time.
// No point in locking as it should be idempotent.
if (fgSystemDefaultCenturyStart == fgSystemDefaultCentury) {
UErrorCode status = U_ZERO_ERROR;
UErrorCode status = U_ZERO_ERROR;
IndianCalendar calendar(Locale("@calendar=Indian"),status);
if (U_SUCCESS(status)) {
calendar.setTime(Calendar::getNow(), status);
calendar.add(UCAL_YEAR, -80, status);
IndianCalendar calendar ( Locale ( "@calendar=Indian" ), status);
if ( U_SUCCESS ( status ) ) {
calendar.setTime ( Calendar::getNow(), status );
calendar.add ( UCAL_YEAR, -80, status );
UDate newStart = calendar.getTime(status);
int32_t newYear = calendar.get(UCAL_YEAR, status);
UDate newStart = calendar.getTime ( status );
int32_t newYear = calendar.get ( UCAL_YEAR, status );
{
Mutex m;
fgSystemDefaultCenturyStart = newStart;
fgSystemDefaultCenturyStartYear = newYear;
}
}
// We have no recourse upon failure unless we want to propagate the failure
// out.
gSystemDefaultCenturyStart = newStart;
gSystemDefaultCenturyStartYear = newYear;
}
// We have no recourse upon failure.
}
UDate
IndianCalendar::defaultCenturyStart() const
{
// lazy-evaluate systemDefaultCenturyStart
umtx_initOnce(gSystemDefaultCenturyInit, &initializeSystemDefaultCentury);
return gSystemDefaultCenturyStart;
}
int32_t
IndianCalendar::defaultCenturyStartYear() const
{
// lazy-evaluate systemDefaultCenturyStartYear
umtx_initOnce(gSystemDefaultCenturyInit, &initializeSystemDefaultCentury);
return gSystemDefaultCenturyStartYear;
}
UOBJECT_DEFINE_RTTI_IMPLEMENTATION(IndianCalendar)
U_NAMESPACE_END

View file

@ -68,7 +68,7 @@ U_NAMESPACE_BEGIN
*/
class IndianCalendar : public Calendar {
class U_I18N_API IndianCalendar : public Calendar {
public:
/**
* Useful constants for IndianCalendar.
@ -274,10 +274,10 @@ public:
* @return The class ID for all objects of this class.
* @internal
*/
U_I18N_API static UClassID U_EXPORT2 getStaticClassID(void);
static UClassID U_EXPORT2 getStaticClassID(void);
/**
* return the calendar type, "buddhist".
* return the calendar type, "indian".
*
* @return calendar type
* @internal
@ -320,49 +320,6 @@ protected:
* @internal
*/
virtual int32_t defaultCenturyStartYear() const;
private: // default century stuff.
/**
* The system maintains a static default century start date. This is initialized
* the first time it is used. Before then, it is set to SYSTEM_DEFAULT_CENTURY to
* indicate an uninitialized state. Once the system default century date and year
* are set, they do not change.
*/
static UDate fgSystemDefaultCenturyStart;
/**
* See documentation for systemDefaultCenturyStart.
*/
static int32_t fgSystemDefaultCenturyStartYear;
/**
* Default value that indicates the defaultCenturyStartYear is unitialized
*/
static const int32_t fgSystemDefaultCenturyYear;
/**
* start of default century, as a date
*/
static const UDate fgSystemDefaultCentury;
/**
* Returns the beginning date of the 100-year window that dates
* with 2-digit years are considered to fall within.
*/
UDate internalGetDefaultCenturyStart(void) const;
/**
* Returns the first year of the 100-year window that dates with
* 2-digit years are considered to fall within.
*/
int32_t internalGetDefaultCenturyStartYear(void) const;
/**
* Initializes the 100-year window that dates with 2-digit years
* are considered to fall within so that its start date is 80 years
* before the current time.
*/
static void initializeSystemDefaultCentury(void);
};
U_NAMESPACE_END

View file

@ -13,6 +13,7 @@
#include "umutex.h"
#include "cmemory.h"
#include "cstring.h"
#include "indiancal.h"
#include "uparse.h"
#include "unicode/localpointer.h"
#include "unicode/resbund.h"
@ -82,6 +83,7 @@ void MultithreadTest::runIndexedTest( int32_t index, UBool exec,
TESTCASE_AUTO(TestBreakTranslit);
TESTCASE_AUTO(TestIncDec);
#endif /* #if !UCONFIG_NO_TRANSLITERATION */
TESTCASE_AUTO(Test20104);
TESTCASE_AUTO_END
}
@ -1549,4 +1551,35 @@ void MultithreadTest::TestIncDec()
}
static Calendar *gSharedCalendar = {};
class Test20104Thread : public SimpleThread {
public:
Test20104Thread() { };
virtual void run();
};
void Test20104Thread::run() {
gSharedCalendar->defaultCenturyStartYear();
}
void MultithreadTest::Test20104() {
UErrorCode status = U_ZERO_ERROR;
Locale loc("hi_IN");
gSharedCalendar = new IndianCalendar(loc, status);
assertSuccess("Test20104", status);
static constexpr int NUM_THREADS = 4;
Test20104Thread threads[NUM_THREADS];
for (auto &thread:threads) {
thread.start();
}
for (auto &thread:threads) {
thread.join();
}
delete gSharedCalendar;
// Note: failure is reported by Thread Sanitizer. Test itself succeeds.
}
#endif /* !UCONFIG_NO_TRANSLITERATION */

View file

@ -53,6 +53,7 @@ public:
void TestUnifiedCache();
void TestBreakTranslit();
void TestIncDec();
void Test20104();
};
#endif