]> granicus.if.org Git - icu/commitdiff
ICU-10673 AnyTransliterator thread safety fix.
authorAndy Heninger <andy.heninger@gmail.com>
Thu, 27 Feb 2014 18:58:29 +0000 (18:58 +0000)
committerAndy Heninger <andy.heninger@gmail.com>
Thu, 27 Feb 2014 18:58:29 +0000 (18:58 +0000)
X-SVN-Rev: 35249

icu4j/main/classes/translit/src/com/ibm/icu/text/AnyTransliterator.java
icu4j/main/tests/translit/src/com/ibm/icu/dev/test/translit/ThreadTest.java

index 0e7edce0368a99cac3d2959b7d197846bad7ae1e..435a88e7cb0e62a9ccda1d9fbffae51be4fc042a 100644 (file)
@@ -1,6 +1,6 @@
 /*
 *****************************************************************
-* Copyright (c) 2002-2011, International Business Machines Corporation
+* Copyright (c) 2002-2014, International Business Machines Corporation
 * and others.  All Rights Reserved.
 *****************************************************************
 * Date        Name        Description
@@ -14,9 +14,9 @@ import java.util.Enumeration;
 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;
 /**
@@ -53,7 +53,7 @@ class AnyTransliterator extends Transliterator {
     /**
      * Cache mapping UScriptCode values to Transliterator*.
      */
-    private Map<Integer, Transliterator> cache;
+    private ConcurrentHashMap<Integer, Transliterator> cache;
 
     /**
      * The target or target/variant string.
@@ -135,7 +135,7 @@ class AnyTransliterator extends Transliterator {
                               int theTargetScript) {
         super(id, null);
         targetScript = theTargetScript;
-        cache = new HashMap<Integer, Transliterator>();
+        cache = new ConcurrentHashMap<Integer, Transliterator>();
 
         target = theTarget;
         if (theVariant.length() > 0) {
@@ -153,7 +153,7 @@ class AnyTransliterator extends Transliterator {
      * @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;
@@ -202,7 +202,10 @@ class AnyTransliterator extends Transliterator {
                     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;
             }
index 1d93685a06238321525e83766d10466f37a81635..31c707bf7a6cd1b44490de0938d9c58fbdf12bef 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *******************************************************************************
- * Copyright (C) 2010, International Business Machines Corporation and         *
+ * Copyright (C) 2014, International Business Machines Corporation and         *
  * others. All Rights Reserved.                                                *
  *******************************************************************************
  */
@@ -62,5 +62,32 @@ public class ThreadTest extends TransliteratorTest {
             }
         }
     }
+    
+    // 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);
+            }
+        }
+    }
 
 }