jdk/test/java/util/concurrent/ConcurrentMap/ConcurrentRemoveIf.java
changeset 30441 415a5242e046
child 43522 f9c6f543c4db
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/concurrent/ConcurrentMap/ConcurrentRemoveIf.java	Tue May 12 10:50:40 2015 +0200
@@ -0,0 +1,176 @@
+/*
+ * Copyright (c) 2015, 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
+ * @run testng ConcurrentRemoveIf
+ * @bug 8078645
+ * @summary Test removeIf on views of concurrent maps
+ */
+
+import org.testng.Assert;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.ConcurrentNavigableMap;
+import java.util.concurrent.ConcurrentSkipListMap;
+import java.util.concurrent.CyclicBarrier;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+
+@Test
+public class ConcurrentRemoveIf {
+    static final int K = 100;
+    static final int SIZE = 1000;
+    static final int HALF_SIZE = SIZE / 2;
+
+    @DataProvider(name = "String,Supplier<ConcurrentMap>,Runnable")
+    public static Object[][] spliteratorDataProvider() {
+        List<Object[]> rows = new ArrayList<>();
+
+        // ConcurrentMap classes to test
+        Map<String, Supplier<ConcurrentMap<Integer, Integer>>> maps = new HashMap<>();
+        maps.put("ConcurrentHashMap", ConcurrentHashMap::new);
+        maps.put("ConcurrentSkipListMap", ConcurrentSkipListMap::new);
+
+        // ConcurrentMap actions
+        Map<String, Consumer<ConcurrentMap<Integer, Integer>>> actions = new HashMap<>();
+        actions.put(".entrySet().removeIf()", m -> m.entrySet().removeIf(e -> e.getValue() == 0));
+        actions.put(".values().removeIf()", m -> m.values().removeIf(v -> v == 0));
+
+        // ConcurrentNavigableMap actions
+        Map<String, Consumer<ConcurrentNavigableMap<Integer, Integer>>> navActions = new HashMap<>();
+        navActions.put(".headMap()/tailMap().entrySet().removeIf()",
+                       m -> {
+                           ConcurrentMap<Integer, Integer> left = m.headMap(HALF_SIZE, false);
+                           ConcurrentMap<Integer, Integer> right = m.tailMap(HALF_SIZE, true);
+                           left.entrySet().removeIf(e -> e.getValue() == 0);
+                           right.entrySet().removeIf(e -> e.getValue() == 0);
+                       });
+        navActions.put(".headMap()/tailMap().values().removeIf()",
+                       m -> {
+                           ConcurrentMap<Integer, Integer> left = m.headMap(HALF_SIZE, false);
+                           ConcurrentMap<Integer, Integer> right = m.tailMap(HALF_SIZE, true);
+                           left.values().removeIf(v -> v == 0);
+                           right.values().removeIf(v -> v == 0);
+                       });
+        navActions.put(".descendingMap().entrySet().removeIf()",
+                       m -> {
+                           ConcurrentMap<Integer, Integer> dm = m.descendingMap();
+                           dm.entrySet().removeIf(e -> e.getValue() == 0);
+                       });
+        navActions.put(".descendingMap().values().removeIf()",
+                       m -> {
+                           ConcurrentMap<Integer, Integer> dm = m.descendingMap();
+                           dm.values().removeIf(v -> v == 0);
+                       });
+
+        for (Map.Entry<String, Supplier<ConcurrentMap<Integer, Integer>>> mapSupplier : maps.entrySet()) {
+            Supplier<ConcurrentMap<Integer, Integer>> sm = mapSupplier.getValue();
+            for (Map.Entry<String, Consumer<ConcurrentMap<Integer, Integer>>> action : actions.entrySet()) {
+                rows.add(new Object[]{
+                        mapSupplier.getKey() + action.getKey(),
+                        sm,
+                        action.getValue()});
+            }
+
+            if (sm.get() instanceof ConcurrentNavigableMap) {
+                for (Map.Entry<String, Consumer<ConcurrentNavigableMap<Integer, Integer>>> action : navActions.entrySet()) {
+                    rows.add(new Object[]{
+                            mapSupplier.getKey() + action.getKey(),
+                            sm,
+                            action.getValue()});
+                }
+            }
+        }
+
+        return rows.toArray(new Object[0][]);
+    }
+
+    ExecutorService executorService = Executors.newCachedThreadPool();
+
+    @AfterClass
+    public void after() {
+        executorService.shutdown();
+    }
+
+    @Test(dataProvider = "String,Supplier<ConcurrentMap>,Runnable")
+    public void testMap(String desc, Supplier<ConcurrentMap<Integer, Integer>> ms, Consumer<ConcurrentMap<Integer, Integer>> action)
+            throws InterruptedException {
+        for (int i = 0; i < K; i++) {
+            testMap(ms.get(), action);
+        }
+    }
+
+    private void testMap(ConcurrentMap<Integer, Integer> map, Consumer<ConcurrentMap<Integer, Integer>> action)
+            throws InterruptedException {
+        // put 0's
+        fillMap(map, 0);
+
+        // To start working simultaneously
+        CyclicBarrier threadStarted = new CyclicBarrier(2);
+
+        // This task put 1's into map
+        CompletableFuture<Void> putter = CompletableFuture.runAsync(
+                awaitOn(threadStarted, () -> fillMap(map, 1)),
+                executorService);
+
+        // This task performs the map action to remove all 0's from map
+        CompletableFuture<Void> remover = CompletableFuture.runAsync(
+                awaitOn(threadStarted, () -> action.accept(map)),
+                executorService);
+
+        // Wait for both tasks to complete
+        CompletableFuture.allOf(putter, remover).join();
+
+        Assert.assertEquals(map.size(), SIZE, "Map size incorrect");
+    }
+
+    static void fillMap(ConcurrentMap<Integer, Integer> map, int value) {
+        for (int i = 0; i < SIZE; i++) {
+            map.put(i, value);
+        }
+    }
+
+    static Runnable awaitOn(CyclicBarrier threadStarted, Runnable r) {
+        return () -> {
+            try {
+                threadStarted.await();
+            }
+            catch (Exception e) {
+                throw new RuntimeException(e);
+            }
+            r.run();
+        };
+    }
+}