diff --git a/icu4c/source/i18n/timezone.cpp b/icu4c/source/i18n/timezone.cpp index f129d8b6076..38e6d64132d 100644 --- a/icu4c/source/i18n/timezone.cpp +++ b/icu4c/source/i18n/timezone.cpp @@ -527,6 +527,11 @@ TimeZone::detectHostTimeZone() // ------------------------------------- +static UMutex *gDefaultZoneMutex() { + static UMutex* m = STATIC_NEW(UMutex); + return m; +} + /** * Initialize DEFAULT_ZONE from the system default time zone. * Upon return, DEFAULT_ZONE will not be NULL, unless operator new() @@ -536,6 +541,7 @@ static void U_CALLCONV initDefault() { ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONE, timeZone_cleanup); + Mutex lock(gDefaultZoneMutex()); // If setDefault() has already been called we can skip getting the // default zone information from the system. if (DEFAULT_ZONE != NULL) { @@ -557,9 +563,6 @@ static void U_CALLCONV initDefault() TimeZone *default_zone = TimeZone::detectHostTimeZone(); - // The only way for DEFAULT_ZONE to be non-null at this point is if the user - // made a thread-unsafe call to setDefault() or adoptDefault() in another - // thread while this thread was doing something that required getting the default. U_ASSERT(DEFAULT_ZONE == NULL); DEFAULT_ZONE = default_zone; @@ -571,7 +574,10 @@ TimeZone* U_EXPORT2 TimeZone::createDefault() { umtx_initOnce(gDefaultZoneInitOnce, initDefault); - return (DEFAULT_ZONE != NULL) ? DEFAULT_ZONE->clone() : NULL; + { + Mutex lock(gDefaultZoneMutex()); + return (DEFAULT_ZONE != NULL) ? DEFAULT_ZONE->clone() : NULL; + } } // ------------------------------------- @@ -581,9 +587,12 @@ TimeZone::adoptDefault(TimeZone* zone) { if (zone != NULL) { - TimeZone *old = DEFAULT_ZONE; - DEFAULT_ZONE = zone; - delete old; + { + Mutex lock(gDefaultZoneMutex()); + TimeZone *old = DEFAULT_ZONE; + DEFAULT_ZONE = zone; + delete old; + } ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONE, timeZone_cleanup); } } diff --git a/icu4c/source/i18n/unicode/timezone.h b/icu4c/source/i18n/unicode/timezone.h index de558f91ac9..ede0c4896df 100644 --- a/icu4c/source/i18n/unicode/timezone.h +++ b/icu4c/source/i18n/unicode/timezone.h @@ -321,10 +321,6 @@ public: * zone is set to the default host time zone. This call adopts the TimeZone object * passed in; the client is no longer responsible for deleting it. * - *
This function is not thread safe. It is an error for multiple threads - * to concurrently attempt to set the default time zone, or for any thread - * to attempt to reference the default zone while another thread is setting it. - * * @param zone A pointer to the new TimeZone object to use as the default. * @stable ICU 2.0 */ @@ -335,8 +331,6 @@ public: * Same as adoptDefault(), except that the TimeZone object passed in is NOT adopted; * the caller remains responsible for deleting it. * - *
See the thread safety note under adoptDefault().
- *
* @param zone The given timezone.
* @system
* @stable ICU 2.0
diff --git a/icu4c/source/test/intltest/tzfmttst.cpp b/icu4c/source/test/intltest/tzfmttst.cpp
index 46a640ae688..056ab6cee47 100644
--- a/icu4c/source/test/intltest/tzfmttst.cpp
+++ b/icu4c/source/test/intltest/tzfmttst.cpp
@@ -85,6 +85,7 @@ TimeZoneFormatTest::runIndexedTest( int32_t index, UBool exec, const char* &name
TESTCASE(5, TestFormatTZDBNames);
TESTCASE(6, TestFormatCustomZone);
TESTCASE(7, TestFormatTZDBNamesAllZoneCoverage);
+ TESTCASE(8, TestAdoptDefaultThreadSafe);
default: name = ""; break;
}
}
@@ -711,6 +712,42 @@ void TimeZoneFormatTest::RunTimeRoundTripTests(int32_t threadNumber) {
delete tzids;
}
+void
+TimeZoneFormatTest::TestAdoptDefaultThreadSafe(void) {
+ ThreadPool