--- jsr166/src/test/tck/Collection8Test.java 2016/11/14 23:52:22 1.24 +++ jsr166/src/test/tck/Collection8Test.java 2016/11/22 01:08:14 1.29 @@ -25,12 +25,14 @@ import java.util.concurrent.CountDownLat import java.util.concurrent.Executors; import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; +import java.util.concurrent.Phaser; import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; import java.util.function.Predicate; +import java.util.stream.Collectors; import junit.framework.Test; @@ -302,7 +304,7 @@ public class Collection8Test extends JSR switch (rnd.nextInt(4)) { case 0: survivors.addAll(c); break; case 1: survivors.addAll(Arrays.asList(c.toArray())); break; - case 2: c.forEach(e -> survivors.add(e)); break; + case 2: c.forEach(survivors::add); break; case 3: for (Object e : c) survivors.add(e); break; } assertTrue(orig.containsAll(accepts)); @@ -353,11 +355,11 @@ public class Collection8Test extends JSR ArrayList forEached = new ArrayList(); ArrayList removeIfed = new ArrayList(); for (Object x : c) iterated.add(x); - c.iterator().forEachRemaining(e -> iteratedForEachRemaining.add(e)); + c.iterator().forEachRemaining(iteratedForEachRemaining::add); for (Spliterator s = c.spliterator(); - s.tryAdvance(e -> tryAdvanced.add(e)); ) {} - c.spliterator().forEachRemaining(e -> spliterated.add(e)); - c.forEach(e -> forEached.add(e)); + s.tryAdvance(tryAdvanced::add); ) {} + c.spliterator().forEachRemaining(spliterated::add); + c.forEach(forEached::add); c.removeIf(e -> { removeIfed.add(e); return false; }); boolean ordered = c.spliterator().hasCharacteristics(Spliterator.ORDERED); @@ -400,7 +402,7 @@ public class Collection8Test extends JSR public void testRemoveAfterForEachRemaining() { Collection c = impl.emptyCollection(); ThreadLocalRandom rnd = ThreadLocalRandom.current(); - { + testCollection: { int n = 3 + rnd.nextInt(2); for (int i = 0; i < n; i++) c.add(impl.makeElement(i)); Iterator it = c.iterator(); @@ -413,7 +415,10 @@ public class Collection8Test extends JSR if (c instanceof java.util.concurrent.ArrayBlockingQueue) { assertIteratorExhausted(it); } else { - it.remove(); + try { it.remove(); } + catch (UnsupportedOperationException ok) { + break testCollection; + } assertEquals(n - 1, c.size()); for (int i = 0; i < n - 1; i++) assertTrue(c.contains(impl.makeElement(i))); @@ -529,32 +534,76 @@ public class Collection8Test extends JSR assertTrue(found.isEmpty()); } - public void testForEachConcurrentStressTest() throws Throwable { + /** + * Motley crew of threads concurrently randomly hammer the collection. + */ + public void testDetectRaces() throws Throwable { if (!impl.isConcurrent()) return; + final ThreadLocalRandom rnd = ThreadLocalRandom.current(); final Collection c = impl.emptyCollection(); final long testDurationMillis = timeoutMillis(); final AtomicBoolean done = new AtomicBoolean(false); - final Object elt = impl.makeElement(1); - final Future f1, f2; + final Object one = impl.makeElement(1); + final Object two = impl.makeElement(2); + final Object[] emptyArray = + (Object[]) java.lang.reflect.Array.newInstance(one.getClass(), 0); + final List> futures; + final Phaser threadsStarted = new Phaser(1); // register this thread + final Runnable[] frobbers = { + () -> c.forEach(x -> assertTrue(x == one || x == two)), + () -> c.stream().forEach(x -> assertTrue(x == one || x == two)), + () -> c.spliterator().trySplit(), + () -> { + Spliterator s = c.spliterator(); + s.tryAdvance(x -> assertTrue(x == one || x == two)); + s.trySplit(); + }, + () -> { + Spliterator s = c.spliterator(); + do {} while (s.tryAdvance(x -> assertTrue(x == one || x == two))); + }, + () -> { + for (Object x : c) assertTrue(x == one || x == two); + }, + () -> { + for (Object x : c.toArray()) assertTrue(x == one || x == two); + }, + () -> { + for (Object x : c.toArray(emptyArray)) assertTrue(x == one || x == two); + }, + () -> { + assertTrue(c.add(one)); + assertTrue(c.contains(one)); + assertTrue(c.remove(one)); + assertFalse(c.contains(one)); + }, + () -> { + assertTrue(c.add(two)); + assertTrue(c.contains(two)); + assertTrue(c.remove(two)); + assertFalse(c.contains(two)); + }, + }; + final List tasks = + Arrays.stream(frobbers) + .filter(task -> rnd.nextBoolean()) // random subset + .map(task -> (Runnable) () -> { + threadsStarted.arriveAndAwaitAdvance(); + while (!done.get()) + task.run(); + }) + .collect(Collectors.toList()); final ExecutorService pool = Executors.newCachedThreadPool(); try (PoolCleaner cleaner = cleaner(pool, done)) { - final CountDownLatch threadsStarted = new CountDownLatch(2); - Runnable checkElt = () -> { - threadsStarted.countDown(); - while (!done.get()) - c.forEach(x -> assertSame(x, elt)); }; - Runnable addRemove = () -> { - threadsStarted.countDown(); - while (!done.get()) { - assertTrue(c.add(elt)); - assertTrue(c.remove(elt)); - }}; - f1 = pool.submit(checkElt); - f2 = pool.submit(addRemove); + threadsStarted.bulkRegister(tasks.size()); + futures = tasks.stream() + .map(pool::submit) + .collect(Collectors.toList()); + threadsStarted.arriveAndDeregister(); Thread.sleep(testDurationMillis); } - assertNull(f1.get(0L, MILLISECONDS)); - assertNull(f2.get(0L, MILLISECONDS)); + for (Future future : futures) + assertNull(future.get(0L, MILLISECONDS)); } // public void testCollection8DebugFail() {