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 SpliteratorTraverseAddRemoveTest 27 * @bug 8085978 28 * @summary repeatedly traverse the queue using the spliterator while 29 * concurrently adding and removing an element to test that self-linked 30 * nodes are never erroneously reported on traversal 31 */ 32 33import org.testng.Assert; 34import org.testng.annotations.DataProvider; 35import org.testng.annotations.Test; 36 37import java.util.ArrayList; 38import java.util.List; 39import java.util.Queue; 40import java.util.Spliterator; 41import java.util.concurrent.CompletableFuture; 42import java.util.concurrent.LinkedTransferQueue; 43import java.util.concurrent.TimeUnit; 44import java.util.concurrent.atomic.AtomicBoolean; 45import java.util.function.Consumer; 46 47@Test 48public class SpliteratorTraverseAddRemoveTest { 49 50 static Object[] of(String desc, Consumer<Queue<?>> c) { 51 return new Object[]{desc, c}; 52 } 53 54 static void assertIsString(Object e) { 55 Assert.assertTrue(e instanceof String, 56 String.format("Object instanceof %s (actual: instanceof %s)", 57 String.class.getName(), 58 e.getClass().getName())); 59 } 60 61 @DataProvider() 62 public static Object[][] spliteratorTraversers() { 63 return new Object[][]{ 64 of("forEachRemaining", q -> { 65 q.spliterator().forEachRemaining(SpliteratorTraverseAddRemoveTest::assertIsString); 66 }), 67 of("tryAdvance", q -> { 68 Spliterator<?> s = q.spliterator(); 69 while (s.tryAdvance(SpliteratorTraverseAddRemoveTest::assertIsString)) 70 ; 71 }), 72 of("trySplit then forEachRemaining", q -> { 73 Spliterator<?> r = q.spliterator(); 74 75 List<Spliterator<?>> ss = new ArrayList<>(); 76 Spliterator<?> l; 77 while ((l = r.trySplit()) != null) { 78 ss.add(l); 79 } 80 ss.add(r); 81 82 ss.forEach(s -> s.forEachRemaining(SpliteratorTraverseAddRemoveTest::assertIsString)); 83 }), 84 }; 85 } 86 87 @Test(dataProvider = "spliteratorTraversers") 88 public void testQueue(String desc, Consumer<Queue<String>> c) 89 throws InterruptedException { 90 AtomicBoolean done = new AtomicBoolean(false); 91 Queue<String> msgs = new LinkedTransferQueue<>(); 92 93 CompletableFuture<Void> traversalTask = CompletableFuture.runAsync(() -> { 94 while (!done.get()) { 95 // Traversal will fail if self-linked nodes of 96 // LinkedTransferQueue are erroneously reported 97 c.accept(msgs); 98 } 99 }); 100 CompletableFuture<Void> addAndRemoveTask = CompletableFuture.runAsync(() -> { 101 while (!traversalTask.isDone()) { 102 msgs.add("msg"); 103 msgs.remove("msg"); 104 } 105 }); 106 107 Thread.sleep(TimeUnit.SECONDS.toMillis(1)); 108 done.set(true); 109 110 addAndRemoveTask.join(); 111 Assert.assertTrue(traversalTask.isDone()); 112 traversalTask.join(); 113 } 114} 115