/*
*******************************************************************************
- * Copyright (C) 2004-2015, Google Inc, International Business Machines *
+ * Copyright (C) 2004-2016, Google Inc, International Business Machines *
* Corporation and others. All Rights Reserved. *
*******************************************************************************
*/
// Used to pre-fill the cache. These same constants appear in MeasureFormat too.
private static final String[] unitKeys = new String[]{"units", "unitsShort", "unitsNarrow"};
+ // Cache of MeasureUnits.
+ // All access to the cache or cacheIsPopulated flag must be synchronized on class MeasureUnit,
+ // i.e. from synchronized static methods. Beware of non-static methods.
private static final Map<String, Map<String,MeasureUnit>> cache
- = new HashMap<String, Map<String,MeasureUnit>>();
+ = new HashMap<String, Map<String,MeasureUnit>>();
+ private static boolean cacheIsPopulated = false;
/**
* @internal
* @stable ICU 53
*/
public synchronized static Set<String> getAvailableTypes() {
+ populateCache();
return Collections.unmodifiableSet(cache.keySet());
}
* @stable ICU 53
*/
public synchronized static Set<MeasureUnit> getAvailable(String type) {
+ populateCache();
Map<String, MeasureUnit> units = cache.get(type);
// Train users not to modify returned set from the start giving us more
// flexibility for implementation.
}
};
- static {
+ /**
+ * Populate the MeasureUnit cache with all types from the data.
+ * Population is done lazily, in response to MeasureUnit.getAvailable()
+ * or other API that expects to see all of the MeasureUnits.
+ *
+ * <p>At static initialization time the MeasureUnits cache is populated
+ * with public static instances (G_FORCE, METER_PER_SECOND_SQUARED, etc.) only.
+ * Adding of others is deferred until later to avoid circular static init
+ * dependencies with classes Currency and TimeUnit.
+ *
+ * <p>Synchronization: this function must be called from static synchronized methods only.
+ *
+ * @internal
+ */
+ static private void populateCache() {
// load all of the units for English, since we know that that is a superset.
/**
* units{
* other{"{0} дена"}
* }
*/
+ if (cacheIsPopulated) {
+ return;
+ }
ICUResourceBundle resource = (ICUResourceBundle)UResourceBundle.getBundleInstance(ICUResourceBundle.ICU_BASE_NAME, "en");
for (String key : unitKeys) {
try {
} catch (MissingResourceException e) {
// fall through
}
+ cacheIsPopulated = true;
}
- // Must only be called at static initialization, or inside synchronized block.
/**
* @internal
* @deprecated This API is ICU internal only.
/*
*******************************************************************************
- * Copyright (C) 2013-2015, International Business Machines Corporation and
+ * Copyright (C) 2013-2016, International Business Machines Corporation and
* others. All Rights Reserved.
*******************************************************************************
*/
import java.util.List;
import java.util.Locale;
import java.util.Map;
+import java.util.Set;
import java.util.TreeMap;
import com.ibm.icu.dev.test.TestFmwk;
*/
public static void main(String[] args) {
//generateConstants(); if (true) return;
+
+ // Ticket #12034 deadlock on multi-threaded static init of MeasureUnit.
+ // The code below reliably deadlocks with ICU 56.
+ // The test is here in main() rather than in a test function so it can be made to run
+ // before anything else.
+ Thread thread = new Thread() {
+ @Override
+ public void run() {
+ Set<String> measureUnitTypes = MeasureUnit.getAvailableTypes();
+ }
+ };
+ thread.start();
+ Currency cur = Currency.getInstance(ULocale.ENGLISH);
+ try {thread.join();} catch(InterruptedException e) {};
+ // System.out.println("Done with MeasureUnit thread test.");
+
new MeasureUnitTest().run(args);
}
Measure twoDeg = new Measure(2, MeasureUnit.GENERIC_TEMPERATURE);
assertEquals("2 deg temp in fr_CA", "2°", mf.format(twoDeg));
}
+
+ public void testPopulateCache() {
+ // Quick check that the lazily added additions to the MeasureUnit cache are present.
+ assertTrue("MeasureUnit: unexpectedly few currencies defined", MeasureUnit.getAvailable("currency").size() > 50);
+ }
// DO NOT DELETE THIS FUNCTION! It may appear as dead code, but we use this to generate code
// for MeasureFormat during the release process.