From ab68202313704a415363856ec291382267d40d99 Mon Sep 17 00:00:00 2001 From: Mike Duigou Date: Thu, 24 Jul 2014 09:01:00 -0700 Subject: [PATCH] 8048209: Collections.synchronizedNavigableSet().tailSet(Object,boolean) synchronizes on wrong object Reviewed-by: psandoz, chegar --- .../share/classes/java/util/Collections.java | 2 +- .../java/util/Collections/SyncSubMutexes.java | 270 ++++++++++++++++++ 2 files changed, 271 insertions(+), 1 deletion(-) create mode 100644 jdk/test/java/util/Collections/SyncSubMutexes.java diff --git a/jdk/src/share/classes/java/util/Collections.java b/jdk/src/share/classes/java/util/Collections.java index f6a65c7b106..7a8404b40f1 100644 --- a/jdk/src/share/classes/java/util/Collections.java +++ b/jdk/src/share/classes/java/util/Collections.java @@ -2342,7 +2342,7 @@ public class Collections { public NavigableSet tailSet(E fromElement, boolean inclusive) { synchronized (mutex) { - return new SynchronizedNavigableSet<>(ns.tailSet(fromElement, inclusive)); + return new SynchronizedNavigableSet<>(ns.tailSet(fromElement, inclusive), mutex); } } } diff --git a/jdk/test/java/util/Collections/SyncSubMutexes.java b/jdk/test/java/util/Collections/SyncSubMutexes.java new file mode 100644 index 00000000000..05dbcc3df71 --- /dev/null +++ b/jdk/test/java/util/Collections/SyncSubMutexes.java @@ -0,0 +1,270 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * @test + * @bug 8048209 + * @summary Check that Collections.synchronizedNavigableSet().tailSet() is using + * the same lock object as it's source. + * @run testng SyncSubMutexes + */ +import java.lang.reflect.Field; +import java.util.*; +import java.util.Set; +import java.util.Arrays; + +import org.testng.annotations.Test; +import org.testng.annotations.DataProvider; +import static org.testng.Assert.assertSame; + +public class SyncSubMutexes { + + @Test(dataProvider = "Collections") + public void testCollections(Collection instance) { + // nothing to test, no subset methods + } + + @Test(dataProvider = "Lists") + public void testLists(List instance) { + assertSame(getSyncCollectionMutex(instance.subList(0, 1)), getSyncCollectionMutex(instance)); + } + + @Test(dataProvider = "Sets") + public void testSets(Set instance) { + // nothing to test, no subset methods + + } + + @Test(dataProvider = "SortedSets") + public void testSortedSets(SortedSet instance) { + assertSame(getSyncCollectionMutex(instance.headSet("Echo")), getSyncCollectionMutex(instance)); + assertSame(getSyncCollectionMutex(instance.tailSet("Charlie")), getSyncCollectionMutex(instance)); + assertSame(getSyncCollectionMutex(instance.subSet("Charlie", "Echo")), getSyncCollectionMutex(instance)); + + } + + @Test(dataProvider = "NavigableSets") + public void testNavigableSets(NavigableSet instance) { + assertSame(getSyncCollectionMutex(instance.descendingSet()), getSyncCollectionMutex(instance)); + assertSame(getSyncCollectionMutex(instance.headSet("Echo")), getSyncCollectionMutex(instance)); + assertSame(getSyncCollectionMutex(instance.headSet("Echo", true)), getSyncCollectionMutex(instance)); + assertSame(getSyncCollectionMutex(instance.tailSet("Charlie")), getSyncCollectionMutex(instance)); + assertSame(getSyncCollectionMutex(instance.tailSet("Charlie", true)), getSyncCollectionMutex(instance)); + assertSame(getSyncCollectionMutex(instance.subSet("Charlie", "Echo")), getSyncCollectionMutex(instance)); + assertSame(getSyncCollectionMutex(instance.subSet("Charlie", true, "Echo", true)), getSyncCollectionMutex(instance)); + } + + @Test(dataProvider = "Maps") + public void testMaps(Map instance) { + assertSame(getSyncCollectionMutex(instance.entrySet()), getSyncMapMutex(instance)); + assertSame(getSyncCollectionMutex(instance.keySet()), getSyncMapMutex(instance)); + assertSame(getSyncCollectionMutex(instance.values()), getSyncMapMutex(instance)); + } + + @Test(dataProvider = "SortedMaps") + public void testSortedMaps(SortedMap instance) { + assertSame(getSyncCollectionMutex(instance.entrySet()), getSyncMapMutex(instance)); + assertSame(getSyncCollectionMutex(instance.keySet()), getSyncMapMutex(instance)); + assertSame(getSyncCollectionMutex(instance.values()), getSyncMapMutex(instance)); + assertSame(getSyncMapMutex(instance.headMap("Echo")), getSyncMapMutex(instance)); + assertSame(getSyncMapMutex(instance.tailMap("Charlie")), getSyncMapMutex(instance)); + assertSame(getSyncMapMutex(instance.subMap("Charlie", "Echo")), getSyncMapMutex(instance)); + } + + @Test(dataProvider = "NavigableMaps") + public void testNavigableMaps(NavigableMap instance) { + assertSame(getSyncMapMutex(instance.descendingMap()), getSyncMapMutex(instance)); + assertSame(getSyncCollectionMutex(instance.entrySet()), getSyncMapMutex(instance)); + assertSame(getSyncCollectionMutex(instance.keySet()), getSyncMapMutex(instance)); + assertSame(getSyncCollectionMutex(instance.descendingKeySet()), getSyncMapMutex(instance)); + assertSame(getSyncCollectionMutex(instance.values()), getSyncMapMutex(instance)); + assertSame(getSyncMapMutex(instance.headMap("Echo")), getSyncMapMutex(instance)); + assertSame(getSyncMapMutex(instance.headMap("Echo", true)), getSyncMapMutex(instance)); + assertSame(getSyncMapMutex(instance.tailMap("Charlie")), getSyncMapMutex(instance)); + assertSame(getSyncMapMutex(instance.tailMap("Charlie", true)), getSyncMapMutex(instance)); + assertSame(getSyncMapMutex(instance.subMap("Charlie", true, "Echo", true)), getSyncMapMutex(instance)); + assertSame(getSyncMapMutex(instance.subMap("Charlie", true, "Echo", true)), getSyncMapMutex(instance)); + } + + @DataProvider(name = "Collections", parallel = true) + public static Iterator collectionProvider() { + return makeCollections().iterator(); + } + + @DataProvider(name = "Lists", parallel = true) + public static Iterator listProvider() { + return makeLists().iterator(); + } + + @DataProvider(name = "Sets", parallel = true) + public static Iterator setProvider() { + return makeSets().iterator(); + } + + @DataProvider(name = "SortedSets", parallel = true) + public static Iterator sortedsetProvider() { + return makeSortedSets().iterator(); + } + + @DataProvider(name = "NavigableSets", parallel = true) + public static Iterator navigablesetProvider() { + return makeNavigableSets().iterator(); + } + + @DataProvider(name = "Maps", parallel = true) + public static Iterator mapProvider() { + return makeMaps().iterator(); + } + + @DataProvider(name = "SortedMaps", parallel = true) + public static Iterator sortedmapProvider() { + return makeSortedMaps().iterator(); + } + + @DataProvider(name = "NavigableMaps", parallel = true) + public static Iterator navigablemapProvider() { + return makeNavigableMaps().iterator(); + } + + private static final Collection BASE_COLLECTION = Collections.unmodifiableCollection( + Arrays.asList("Alpha", "Bravo", "Charlie", "Delta", "Echo", "Foxtrot", "Golf") + ); + private static final Map BASE_MAP; + + static { + Map map = new HashMap<>(); + for(String each : BASE_COLLECTION) { + map.put(each, "*" + each + "*"); + } + BASE_MAP = Collections.unmodifiableMap(map); + } + + public static Collection makeCollections() { + Collection instances = new ArrayList<>(); + instances.add(new Object[] {Collections.synchronizedCollection(new ArrayList<>(BASE_COLLECTION))}); + instances.addAll(makeLists()); + + return instances; + } + + public static Collection makeLists() { + Collection instances = new ArrayList<>(); + instances.add(new Object[] {Collections.synchronizedList(new ArrayList<>(BASE_COLLECTION))}); + instances.add(new Object[] {Collections.synchronizedList(new ArrayList<>(BASE_COLLECTION)).subList(1, 2)}); + + return instances; + } + + public static Collection makeSets() { + Collection instances = new ArrayList<>(); + + instances.add(new Object[] {Collections.synchronizedSet(new TreeSet<>(BASE_COLLECTION))}); + instances.addAll(makeSortedSets()); + return instances; + } + + public static Collection makeSortedSets() { + Collection instances = new ArrayList<>(); + instances.add(new Object[] {Collections.synchronizedSortedSet(new TreeSet<>(BASE_COLLECTION))}); + instances.add(new Object[] {Collections.synchronizedSortedSet(new TreeSet<>(BASE_COLLECTION)).headSet("Foxtrot")}); + instances.add(new Object[] {Collections.synchronizedSortedSet(new TreeSet<>(BASE_COLLECTION)).tailSet("Bravo")}); + instances.add(new Object[] {Collections.synchronizedSortedSet(new TreeSet<>(BASE_COLLECTION)).subSet("Bravo", "Foxtrot")}); + instances.addAll(makeNavigableSets()); + + return instances; + } + + public static Collection makeNavigableSets() { + Collection instances = new ArrayList<>(); + + instances.add(new Object[] {Collections.synchronizedNavigableSet(new TreeSet<>(BASE_COLLECTION))}); + instances.add(new Object[] {Collections.synchronizedNavigableSet(new TreeSet<>(BASE_COLLECTION)).descendingSet().descendingSet()}); + instances.add(new Object[] {Collections.synchronizedNavigableSet(new TreeSet<>(BASE_COLLECTION)).headSet("Foxtrot")}); + instances.add(new Object[] {Collections.synchronizedNavigableSet(new TreeSet<>(BASE_COLLECTION)).headSet("Foxtrot", true)}); + instances.add(new Object[] {Collections.synchronizedNavigableSet(new TreeSet<>(BASE_COLLECTION)).tailSet("Bravo")}); + instances.add(new Object[] {Collections.synchronizedNavigableSet(new TreeSet<>(BASE_COLLECTION)).tailSet("Bravo", true)}); + instances.add(new Object[] {Collections.synchronizedNavigableSet(new TreeSet<>(BASE_COLLECTION)).subSet("Bravo", "Foxtrot")}); + instances.add(new Object[] {Collections.synchronizedNavigableSet(new TreeSet<>(BASE_COLLECTION)).subSet("Bravo", true, "Foxtrot", true)}); + + return instances; + } + + public static Collection makeMaps() { + Collection instances = new ArrayList<>(); + + instances.add(new Object[] {Collections.synchronizedMap(new HashMap<>(BASE_MAP))}); + instances.addAll(makeSortedMaps()); + + return instances; + } + + public static Collection makeSortedMaps() { + Collection instances = new ArrayList<>(); + + instances.add(new Object[] {Collections.synchronizedSortedMap(new TreeMap<>(BASE_MAP))}); + instances.add(new Object[] {Collections.synchronizedSortedMap(new TreeMap<>(BASE_MAP)).headMap("Foxtrot")}); + instances.add(new Object[] {Collections.synchronizedSortedMap(new TreeMap<>(BASE_MAP)).tailMap("Bravo")}); + instances.add(new Object[] {Collections.synchronizedSortedMap(new TreeMap<>(BASE_MAP)).subMap("Bravo", "Foxtrot")}); + instances.addAll(makeNavigableMaps()); + + return instances; + } + + public static Collection makeNavigableMaps() { + Collection instances = new ArrayList<>(); + + instances.add(new Object[] {Collections.synchronizedNavigableMap(new TreeMap<>(BASE_MAP))}); + instances.add(new Object[] {Collections.synchronizedNavigableMap(new TreeMap<>(BASE_MAP).descendingMap().descendingMap())}); + instances.add(new Object[] {Collections.synchronizedNavigableMap(new TreeMap<>(BASE_MAP)).headMap("Foxtrot")}); + instances.add(new Object[] {Collections.synchronizedNavigableMap(new TreeMap<>(BASE_MAP)).headMap("Foxtrot", true)}); + instances.add(new Object[] {Collections.synchronizedNavigableMap(new TreeMap<>(BASE_MAP)).tailMap("Bravo")}); + instances.add(new Object[] {Collections.synchronizedNavigableMap(new TreeMap<>(BASE_MAP)).tailMap("Bravo", true)}); + instances.add(new Object[] {Collections.synchronizedNavigableMap(new TreeMap<>(BASE_MAP)).subMap("Bravo", "Foxtrot")}); + instances.add(new Object[] {Collections.synchronizedNavigableMap(new TreeMap<>(BASE_MAP)).subMap("Bravo", true, "Foxtrot", true)}); + + return instances; + } + + private static Object getSyncCollectionMutex(Collection from) { + try { + Class synchronizedCollectionClazz = Class.forName("java.util.Collections$SynchronizedCollection"); + Field f = synchronizedCollectionClazz.getDeclaredField("mutex"); + f.setAccessible(true); + return f.get(from); + } catch ( ClassNotFoundException | NoSuchFieldException | IllegalAccessException e) { + throw new RuntimeException("Unable to get mutex field.", e); + } + } + + private static Object getSyncMapMutex(Map from) { + try { + Class synchronizedMapClazz = Class.forName("java.util.Collections$SynchronizedMap"); + Field f = synchronizedMapClazz.getDeclaredField("mutex"); + f.setAccessible(true); + return f.get(from); + } catch ( ClassNotFoundException | NoSuchFieldException | IllegalAccessException e) { + throw new RuntimeException("Unable to get mutex field.", e); + } + } + +}