1 /* 2 * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 /* 25 * @test 26 * @run testng ConcurrentRemoveIf 27 * @bug 8078645 28 * @summary Test removeIf on views of concurrent maps 29 */ 30 31 import static org.testng.Assert.assertEquals; 32 import org.testng.annotations.AfterClass; 33 import org.testng.annotations.DataProvider; 34 import org.testng.annotations.Test; 35 36 import java.util.ArrayList; 37 import java.util.HashMap; 38 import java.util.List; 39 import java.util.Map; 40 import java.util.concurrent.CompletableFuture; 41 import java.util.concurrent.ConcurrentHashMap; 42 import java.util.concurrent.ConcurrentMap; 43 import java.util.concurrent.ConcurrentNavigableMap; 44 import java.util.concurrent.ConcurrentSkipListMap; 45 import java.util.concurrent.CyclicBarrier; 46 import java.util.concurrent.ExecutorService; 47 import java.util.concurrent.Executors; 48 import java.util.function.Consumer; 49 import java.util.function.Supplier; 50 51 @Test 52 public class ConcurrentRemoveIf { 53 static final int K = 100; 54 static final int SIZE = 1000; 55 static final int HALF_SIZE = SIZE / 2; 56 57 @DataProvider() concurrentMapViewRemoveIfActions()58 public static Object[][] concurrentMapViewRemoveIfActions() { 59 List<Object[]> rows = new ArrayList<>(); 60 61 // ConcurrentMap classes to test 62 Map<String, Supplier<ConcurrentMap<Integer, Integer>>> maps = new HashMap<>(); 63 maps.put("ConcurrentHashMap", ConcurrentHashMap::new); 64 maps.put("ConcurrentSkipListMap", ConcurrentSkipListMap::new); 65 66 // ConcurrentMap actions 67 Map<String, Consumer<ConcurrentMap<Integer, Integer>>> actions = new HashMap<>(); 68 actions.put(".entrySet().removeIf()", m -> m.entrySet().removeIf(e -> e.getValue() == 0)); 69 actions.put(".values().removeIf()", m -> m.values().removeIf(v -> v == 0)); 70 71 // ConcurrentNavigableMap actions 72 Map<String, Consumer<ConcurrentNavigableMap<Integer, Integer>>> navActions = new HashMap<>(); 73 navActions.put(".headMap()/tailMap().entrySet().removeIf()", 74 m -> { 75 ConcurrentMap<Integer, Integer> left = m.headMap(HALF_SIZE, false); 76 ConcurrentMap<Integer, Integer> right = m.tailMap(HALF_SIZE, true); 77 left.entrySet().removeIf(e -> e.getValue() == 0); 78 right.entrySet().removeIf(e -> e.getValue() == 0); 79 }); 80 navActions.put(".headMap()/tailMap().values().removeIf()", 81 m -> { 82 ConcurrentMap<Integer, Integer> left = m.headMap(HALF_SIZE, false); 83 ConcurrentMap<Integer, Integer> right = m.tailMap(HALF_SIZE, true); 84 left.values().removeIf(v -> v == 0); 85 right.values().removeIf(v -> v == 0); 86 }); 87 navActions.put(".descendingMap().entrySet().removeIf()", 88 m -> { 89 ConcurrentMap<Integer, Integer> dm = m.descendingMap(); 90 dm.entrySet().removeIf(e -> e.getValue() == 0); 91 }); 92 navActions.put(".descendingMap().values().removeIf()", 93 m -> { 94 ConcurrentMap<Integer, Integer> dm = m.descendingMap(); 95 dm.values().removeIf(v -> v == 0); 96 }); 97 98 maps.forEach((mapDescription, sm) -> { 99 actions.forEach((actionDescription, action) -> { 100 rows.add(new Object[] {mapDescription + actionDescription, sm, action}); 101 }); 102 103 if (sm.get() instanceof ConcurrentNavigableMap) { 104 navActions.forEach((actionDescription, action) -> { 105 rows.add(new Object[] {mapDescription + actionDescription, sm, action}); 106 }); 107 } 108 }); 109 110 return rows.toArray(new Object[0][]); 111 } 112 113 ExecutorService executorService = Executors.newCachedThreadPool(); 114 115 @AfterClass after()116 public void after() { 117 executorService.shutdown(); 118 } 119 120 @Test(dataProvider = "concurrentMapViewRemoveIfActions") testMap(String desc, Supplier<ConcurrentMap<Integer, Integer>> ms, Consumer<ConcurrentMap<Integer, Integer>> action)121 public void testMap(String desc, Supplier<ConcurrentMap<Integer, Integer>> ms, Consumer<ConcurrentMap<Integer, Integer>> action) 122 throws InterruptedException { 123 for (int i = 0; i < K; i++) { 124 testMap(ms.get(), action); 125 } 126 } 127 testMap(ConcurrentMap<Integer, Integer> map, Consumer<ConcurrentMap<Integer, Integer>> action)128 private void testMap(ConcurrentMap<Integer, Integer> map, Consumer<ConcurrentMap<Integer, Integer>> action) 129 throws InterruptedException { 130 // put 0's 131 fillMap(map, 0); 132 133 // To start working simultaneously 134 CyclicBarrier threadStarted = new CyclicBarrier(2); 135 136 // This task puts 1's into map 137 CompletableFuture<Void> putter = CompletableFuture.runAsync( 138 awaitOn(threadStarted, () -> fillMap(map, 1)), 139 executorService); 140 141 // This task performs the map action to remove all 0's from map 142 CompletableFuture<Void> remover = CompletableFuture.runAsync( 143 awaitOn(threadStarted, () -> action.accept(map)), 144 executorService); 145 146 // Wait for both tasks to complete 147 CompletableFuture.allOf(putter, remover).join(); 148 149 assertEquals(map.size(), SIZE, "Map size incorrect"); 150 map.forEach((k, v) -> assertEquals(v, (Integer)1)); 151 } 152 fillMap(ConcurrentMap<Integer, Integer> map, int value)153 static void fillMap(ConcurrentMap<Integer, Integer> map, int value) { 154 for (int i = 0; i < SIZE; i++) { 155 map.put(i, value); 156 } 157 } 158 awaitOn(CyclicBarrier threadStarted, Runnable r)159 static Runnable awaitOn(CyclicBarrier threadStarted, Runnable r) { 160 return () -> { 161 try { 162 threadStarted.await(); 163 } 164 catch (Exception e) { 165 throw new RuntimeException(e); 166 } 167 r.run(); 168 }; 169 } 170 } 171