changeset 54962:940046624bf8

8215249: Miscellaneous changes imported from jsr166 CVS 2019-02 Reviewed-by: martin, chegar, dholmes
author dl
date Fri, 15 Feb 2019 11:18:01 -0800
parents d04f72c354e9
children 37c12515c30f
files src/java.base/share/classes/java/util/concurrent/CyclicBarrier.java test/jdk/java/util/Collection/IteratorMicroBenchmark.java test/jdk/java/util/Collection/RemoveMicroBenchmark.java test/jdk/java/util/concurrent/tck/CyclicBarrierTest.java
diffstat 4 files changed, 155 insertions(+), 70 deletions(-) [+]
line wrap: on
line diff
--- a/src/java.base/share/classes/java/util/concurrent/CyclicBarrier.java	Fri Feb 15 11:18:01 2019 -0800
+++ b/src/java.base/share/classes/java/util/concurrent/CyclicBarrier.java	Fri Feb 15 11:18:01 2019 -0800
@@ -98,12 +98,11 @@
  *   }
  * }}</pre>
  *
- * Here, each worker thread processes a row of the matrix then waits at the
- * barrier until all rows have been processed. When all rows are processed
- * the supplied {@link Runnable} barrier action is executed and merges the
- * rows. If the merger
- * determines that a solution has been found then {@code done()} will return
- * {@code true} and each worker will terminate.
+ * Here, each worker thread processes a row of the matrix, then waits at the
+ * barrier until all rows have been processed. When all rows are processed the
+ * supplied {@link Runnable} barrier action is executed and merges the rows.
+ * If the merger determines that a solution has been found then {@code done()}
+ * will return {@code true} and each worker will terminate.
  *
  * <p>If the barrier action does not rely on the parties being suspended when
  * it is executed, then any of the threads in the party could execute that
@@ -132,6 +131,7 @@
  * corresponding {@code await()} in other threads.
  *
  * @see CountDownLatch
+ * @see Phaser
  *
  * @author Doug Lea
  * @since 1.5
@@ -214,18 +214,17 @@
 
             int index = --count;
             if (index == 0) {  // tripped
-                boolean ranAction = false;
-                try {
-                    final Runnable command = barrierCommand;
-                    if (command != null)
+                Runnable command = barrierCommand;
+                if (command != null) {
+                    try {
                         command.run();
-                    ranAction = true;
-                    nextGeneration();
-                    return 0;
-                } finally {
-                    if (!ranAction)
+                    } catch (Throwable ex) {
                         breakBarrier();
+                        throw ex;
+                    }
                 }
+                nextGeneration();
+                return 0;
             }
 
             // loop until tripped, broken, interrupted, or timed out
--- a/test/jdk/java/util/Collection/IteratorMicroBenchmark.java	Fri Feb 15 11:18:01 2019 -0800
+++ b/test/jdk/java/util/Collection/IteratorMicroBenchmark.java	Fri Feb 15 11:18:01 2019 -0800
@@ -69,8 +69,6 @@
  * Be patient; this program runs for a very long time.
  * For faster runs, restrict execution using command line args.
  *
- * This is an interface based version of ArrayList/IteratorMicroBenchmark
- *
  * @author Martin Buchholz
  */
 public class IteratorMicroBenchmark {
@@ -115,7 +113,9 @@
         CountDownLatch finalized = new CountDownLatch(1);
         ReferenceQueue<Object> queue = new ReferenceQueue<>();
         WeakReference<Object> ref = new WeakReference<>(
-            new Object() { protected void finalize() { finalized.countDown(); }},
+            new Object() {
+                @SuppressWarnings("deprecation")
+                protected void finalize() { finalized.countDown(); }},
             queue);
         try {
             for (int tries = 3; tries--> 0; ) {
@@ -267,16 +267,22 @@
             });
     }
 
-    static List<Integer> makeSubList(List<Integer> list) {
+    String goodClassName(Object x) {
+        return goodClassName(x.getClass());
+    }
+
+    static List<Integer> makeSubList(
+        List<Integer> elements,
+        UnaryOperator<List<Integer>> copyConstructor) {
+        final ArrayList<Integer> padded = new ArrayList<>();
         final ThreadLocalRandom rnd = ThreadLocalRandom.current();
-        int size = list.size();
-        if (size <= 2) return list.subList(0, size);
-        List<Integer> subList = list.subList(rnd.nextInt(0, 2),
-                                             size - rnd.nextInt(0, 2));
-        List<Integer> copy = new ArrayList<>(list);
-        subList.clear();
-        subList.addAll(copy);
-        return subList;
+        final int frontPorch = rnd.nextInt(3);
+        final int backPorch = rnd.nextInt(3);
+        for (int n = frontPorch; n--> 0; ) padded.add(rnd.nextInt());
+        padded.addAll(elements);
+        for (int n = backPorch; n--> 0; ) padded.add(rnd.nextInt());
+        return copyConstructor.apply(padded)
+            .subList(frontPorch, frontPorch + elements.size());
     }
 
     void run() throws Throwable {
@@ -297,22 +303,42 @@
             abq.add(abq.remove());
         }
 
-        ArrayList<Job> jobs = Stream.<Collection<Integer>>of(
-                al, ad, abq,
-                makeSubList(new ArrayList<>(al)),
+        final Integer[] array = al.toArray(new Integer[0]);
+        final List<Integer> immutableSubList
+            = makeSubList(al, x -> List.of(x.toArray(new Integer[0])));
+
+        Stream<Collection<Integer>> collections = concatStreams(
+            Stream.of(
+                // Lists and their subLists
+                al,
+                makeSubList(al, ArrayList::new),
+                new Vector<>(al),
+                makeSubList(al, Vector::new),
                 new LinkedList<>(al),
-                makeSubList(new LinkedList<>(al)),
+                makeSubList(al, LinkedList::new),
+                new CopyOnWriteArrayList<>(al),
+                makeSubList(al, CopyOnWriteArrayList::new),
+
+                ad,
                 new PriorityQueue<>(al),
-                new Vector<>(al),
-                makeSubList(new Vector<>(al)),
-                new CopyOnWriteArrayList<>(al),
-                makeSubList(new CopyOnWriteArrayList<>(al)),
                 new ConcurrentLinkedQueue<>(al),
                 new ConcurrentLinkedDeque<>(al),
+
+                // Blocking Queues
+                abq,
                 new LinkedBlockingQueue<>(al),
                 new LinkedBlockingDeque<>(al),
                 new LinkedTransferQueue<>(al),
-                new PriorityBlockingQueue<>(al))
+                new PriorityBlockingQueue<>(al),
+
+                List.of(al.toArray(new Integer[0]))),
+
+            // avoid UnsupportedOperationException in jdk9 and jdk10
+            (goodClassName(immutableSubList).equals("RandomAccessSubList"))
+            ? Stream.empty()
+            : Stream.of(immutableSubList));
+
+        ArrayList<Job> jobs = collections
             .flatMap(x -> jobs(x))
             .filter(job ->
                 nameFilter == null || nameFilter.matcher(job.name()).find())
@@ -329,16 +355,29 @@
         return Stream.of(streams).flatMap(s -> s);
     }
 
+    boolean isMutable(Collection<Integer> x) {
+        return !(x.getClass().getName().contains("ImmutableCollections$"));
+    }
+
     Stream<Job> jobs(Collection<Integer> x) {
+        final String klazz = goodClassName(x);
         return concatStreams(
             collectionJobs(x),
 
+            (isMutable(x))
+            ? mutableCollectionJobs(x)
+            : Stream.empty(),
+
             (x instanceof Deque)
             ? dequeJobs((Deque<Integer>)x)
             : Stream.empty(),
 
             (x instanceof List)
             ? listJobs((List<Integer>)x)
+            : Stream.empty(),
+
+            (x instanceof List && isMutable(x))
+            ? mutableListJobs((List<Integer>)x)
             : Stream.empty());
     }
 
@@ -350,7 +389,7 @@
     }
 
     Stream<Job> collectionJobs(Collection<Integer> x) {
-        final String klazz = goodClassName(x.getClass());
+        final String klazz = goodClassName(x);
         return Stream.of(
             new Job(klazz + " iterate for loop") {
                 public void work() throws Throwable {
@@ -381,14 +420,6 @@
                         sum[0] = 0;
                         x.spliterator().forEachRemaining(n -> sum[0] += n);
                         check.sum(sum[0]);}}},
-            new Job(klazz + " removeIf") {
-                public void work() throws Throwable {
-                    int[] sum = new int[1];
-                    for (int i = 0; i < iterations; i++) {
-                        sum[0] = 0;
-                        if (x.removeIf(n -> { sum[0] += n; return false; }))
-                            throw new AssertionError();
-                        check.sum(sum[0]);}}},
             new Job(klazz + " contains") {
                 public void work() throws Throwable {
                     int[] sum = new int[1];
@@ -407,14 +438,6 @@
                         if (x.containsAll(sneakyAdderCollection))
                             throw new AssertionError();
                         check.sum(sum[0]);}}},
-            new Job(klazz + " remove(Object)") {
-                public void work() throws Throwable {
-                    int[] sum = new int[1];
-                    Object sneakyAdder = sneakyAdder(sum);
-                    for (int i = 0; i < iterations; i++) {
-                        sum[0] = 0;
-                        if (x.remove(sneakyAdder)) throw new AssertionError();
-                        check.sum(sum[0]);}}},
             new Job(klazz + " forEach") {
                 public void work() throws Throwable {
                     int[] sum = new int[1];
@@ -498,8 +521,29 @@
                         check.sum(sum[0]);}}});
     }
 
+    Stream<Job> mutableCollectionJobs(Collection<Integer> x) {
+        final String klazz = goodClassName(x);
+        return Stream.of(
+            new Job(klazz + " removeIf") {
+                public void work() throws Throwable {
+                    int[] sum = new int[1];
+                    for (int i = 0; i < iterations; i++) {
+                        sum[0] = 0;
+                        if (x.removeIf(n -> { sum[0] += n; return false; }))
+                            throw new AssertionError();
+                        check.sum(sum[0]);}}},
+            new Job(klazz + " remove(Object)") {
+                public void work() throws Throwable {
+                    int[] sum = new int[1];
+                    Object sneakyAdder = sneakyAdder(sum);
+                    for (int i = 0; i < iterations; i++) {
+                        sum[0] = 0;
+                        if (x.remove(sneakyAdder)) throw new AssertionError();
+                        check.sum(sum[0]);}}});
+    }
+
     Stream<Job> dequeJobs(Deque<Integer> x) {
-        String klazz = goodClassName(x.getClass());
+        final String klazz = goodClassName(x);
         return Stream.of(
             new Job(klazz + " descendingIterator() loop") {
                 public void work() throws Throwable {
@@ -519,7 +563,7 @@
     }
 
     Stream<Job> listJobs(List<Integer> x) {
-        final String klazz = goodClassName(x.getClass());
+        final String klazz = goodClassName(x);
         return Stream.of(
             new Job(klazz + " listIterator forward loop") {
                 public void work() throws Throwable {
@@ -555,15 +599,6 @@
                         if (x.lastIndexOf(sneakyAdder) != -1)
                             throw new AssertionError();
                         check.sum(sum[0]);}}},
-            new Job(klazz + " replaceAll") {
-                public void work() throws Throwable {
-                    int[] sum = new int[1];
-                    UnaryOperator<Integer> sneakyAdder =
-                        x -> { sum[0] += x; return x; };
-                    for (int i = 0; i < iterations; i++) {
-                        sum[0] = 0;
-                        x.replaceAll(sneakyAdder);
-                        check.sum(sum[0]);}}},
             new Job(klazz + " equals") {
                 public void work() throws Throwable {
                     ArrayList<Integer> copy = new ArrayList<>(x);
@@ -577,4 +612,18 @@
                         if (x.hashCode() != hashCode)
                             throw new AssertionError();}}});
     }
+
+    Stream<Job> mutableListJobs(List<Integer> x) {
+        final String klazz = goodClassName(x);
+        return Stream.of(
+            new Job(klazz + " replaceAll") {
+                public void work() throws Throwable {
+                    int[] sum = new int[1];
+                    UnaryOperator<Integer> sneakyAdder =
+                        x -> { sum[0] += x; return x; };
+                    for (int i = 0; i < iterations; i++) {
+                        sum[0] = 0;
+                        x.replaceAll(sneakyAdder);
+                        check.sum(sum[0]);}}});
+    }
 }
--- a/test/jdk/java/util/Collection/RemoveMicroBenchmark.java	Fri Feb 15 11:18:01 2019 -0800
+++ b/test/jdk/java/util/Collection/RemoveMicroBenchmark.java	Fri Feb 15 11:18:01 2019 -0800
@@ -270,6 +270,10 @@
             });
     }
 
+    String goodClassName(Object x) {
+        return goodClassName(x.getClass());
+    }
+
     static List<Integer> makeSubList(List<Integer> list) {
         final ThreadLocalRandom rnd = ThreadLocalRandom.current();
         int size = rnd.nextInt(4);
@@ -369,7 +373,7 @@
     }
 
     Stream<Job> collectionJobs(Collection<Integer> x) {
-        final String klazz = goodClassName(x.getClass());
+        final String klazz = goodClassName(x);
         return Stream.of(
             new Job(klazz + " removeIf") {
                 public void work() throws Throwable {
@@ -422,7 +426,7 @@
     }
 
     Stream<Job> iteratorRemoveJobs(Collection<Integer> x) {
-        final String klazz = goodClassName(x.getClass());
+        final String klazz = goodClassName(x);
         return Stream.of(
              new Job(klazz + " Iterator.remove") {
                 public void work() throws Throwable {
@@ -460,7 +464,7 @@
     }
 
     Stream<Job> queueJobs(Queue<Integer> x) {
-        final String klazz = goodClassName(x.getClass());
+        final String klazz = goodClassName(x);
         return Stream.of(
             new Job(klazz + " poll()") {
                 public void work() throws Throwable {
@@ -474,7 +478,7 @@
     }
 
     Stream<Job> dequeJobs(Deque<Integer> x) {
-        final String klazz = goodClassName(x.getClass());
+        final String klazz = goodClassName(x);
         return Stream.of(
             new Job(klazz + " descendingIterator().remove") {
                 public void work() throws Throwable {
@@ -509,7 +513,7 @@
     }
 
     Stream<Job> blockingQueueJobs(BlockingQueue<Integer> x) {
-        final String klazz = goodClassName(x.getClass());
+        final String klazz = goodClassName(x);
         return Stream.of(
             new Job(klazz + " timed poll()") {
                 public void work() throws Throwable {
@@ -545,7 +549,7 @@
     }
 
     Stream<Job> blockingDequeJobs(BlockingDeque<Integer> x) {
-        final String klazz = goodClassName(x.getClass());
+        final String klazz = goodClassName(x);
         return Stream.of(
             new Job(klazz + " timed pollFirst()") {
                 public void work() throws Throwable {
--- a/test/jdk/java/util/concurrent/tck/CyclicBarrierTest.java	Fri Feb 15 11:18:01 2019 -0800
+++ b/test/jdk/java/util/concurrent/tck/CyclicBarrierTest.java	Fri Feb 15 11:18:01 2019 -0800
@@ -38,6 +38,9 @@
 import java.util.concurrent.BrokenBarrierException;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.CyclicBarrier;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadLocalRandom;
 import java.util.concurrent.TimeoutException;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
@@ -486,4 +489,34 @@
             assertEquals(0, barrier.getNumberWaiting());
         }
     }
+
+    /**
+     * There can be more threads calling await() than parties, as long as each
+     * task only calls await once and the task count is a multiple of parties.
+     */
+    public void testMoreTasksThanParties() throws Exception {
+        final ThreadLocalRandom rnd = ThreadLocalRandom.current();
+        final int parties = rnd.nextInt(1, 5);
+        final int nTasks = rnd.nextInt(1, 5) * parties;
+        final AtomicInteger tripCount = new AtomicInteger(0);
+        final AtomicInteger awaitCount = new AtomicInteger(0);
+        final CyclicBarrier barrier =
+            new CyclicBarrier(parties, () -> tripCount.getAndIncrement());
+        final ExecutorService e = Executors.newFixedThreadPool(nTasks);
+        final Runnable awaiter = () -> {
+            try {
+                if (ThreadLocalRandom.current().nextBoolean())
+                    barrier.await();
+                else
+                    barrier.await(LONG_DELAY_MS, MILLISECONDS);
+                awaitCount.getAndIncrement();
+            } catch (Throwable fail) { threadUnexpectedException(fail); }};
+        try (PoolCleaner cleaner = cleaner(e)) {
+            for (int i = nTasks; i--> 0; )
+                e.execute(awaiter);
+        }
+        assertEquals(nTasks / parties, tripCount.get());
+        assertEquals(nTasks, awaitCount.get());
+        assertEquals(0, barrier.getNumberWaiting());
+    }
 }