/*
*****************************************************************
-* Copyright (c) 2002-2011, International Business Machines Corporation
+* Copyright (c) 2002-2014, International Business Machines Corporation
* and others. All Rights Reserved.
*****************************************************************
* Date Name Description
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
-import java.util.Map;
import java.util.MissingResourceException;
import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
import com.ibm.icu.lang.UScript;
/**
/**
* Cache mapping UScriptCode values to Transliterator*.
*/
- private Map<Integer, Transliterator> cache;
+ private ConcurrentHashMap<Integer, Transliterator> cache;
/**
* The target or target/variant string.
int theTargetScript) {
super(id, null);
targetScript = theTargetScript;
- cache = new HashMap<Integer, Transliterator>();
+ cache = new ConcurrentHashMap<Integer, Transliterator>();
target = theTarget;
if (theVariant.length() > 0) {
* @param cache2 The Map object for cache.
*/
public AnyTransliterator(String id, UnicodeFilter filter, String target2,
- int targetScript2, Transliterator widthFix2, Map<Integer, Transliterator> cache2) {
+ int targetScript2, Transliterator widthFix2, ConcurrentHashMap<Integer, Transliterator> cache2) {
super(id, filter);
targetScript = targetScript2;
cache = cache2;
v.add(t);
t = new CompoundTransliterator(v);
}
- cache.put(key, t);
+ Transliterator prevCachedT = cache.putIfAbsent(key, t);
+ if (prevCachedT != null) {
+ t = prevCachedT;
+ }
} else if (!isWide(targetScript)) {
return widthFix;
}
/*
*******************************************************************************
- * Copyright (C) 2010, International Business Machines Corporation and *
+ * Copyright (C) 2014, International Business Machines Corporation and *
* others. All Rights Reserved. *
*******************************************************************************
*/
}
}
}
+
+ // Test for ticket #10673, race in cache code in AnyTransliterator.
+ // It's difficult to make the original unsafe code actually fail, but
+ // this test will fairly reliably take the code path for races in
+ // populating the cache.
+ //
+ public void TestAnyTranslit() {
+ final Transliterator tx = Transliterator.getInstance("Any-Latin");
+ ArrayList<Thread> threads = new ArrayList<Thread>();
+ for (int i=0; i<8; i++) {
+ threads.add(new Thread() {
+ public void run() {
+ tx.transliterate("διαφορετικούς");
+ }
+ });
+ }
+ for (Thread th:threads) {
+ th.start();
+ }
+ for (Thread th:threads) {
+ try {
+ th.join();
+ } catch (InterruptedException e) {
+ errln("Uexpected exception: " + e);
+ }
+ }
+ }
}