1 /* 2 * Copyright (c) 2020, 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 AsyncShutdownNowInvokeAnyRace 27 * @summary A variant of AsyncShutdownNow useful for race bug hunting 28 */ 29 30 // TODO: reorganize all of the AsyncShutdown tests 31 32 import java.util.Collections; 33 import java.util.List; 34 import java.util.concurrent.Callable; 35 import java.util.concurrent.CancellationException; 36 import java.util.concurrent.CompletableFuture; 37 import java.util.concurrent.CountDownLatch; 38 import java.util.concurrent.ExecutionException; 39 import java.util.concurrent.ForkJoinPool; 40 import java.util.concurrent.Future; 41 import java.util.concurrent.RejectedExecutionException; 42 import java.util.concurrent.ThreadLocalRandom; 43 import java.util.concurrent.TimeoutException; 44 import java.util.concurrent.TimeUnit; 45 import java.util.concurrent.atomic.AtomicInteger; 46 import java.util.concurrent.atomic.AtomicReference; 47 import static java.util.concurrent.TimeUnit.SECONDS; 48 49 import java.lang.management.ManagementFactory; 50 import java.lang.management.LockInfo; 51 import java.lang.management.ThreadInfo; 52 import java.lang.management.ThreadMXBean; 53 54 import org.testng.annotations.Test; 55 import static org.testng.Assert.*; 56 57 public class AsyncShutdownNowInvokeAnyRace { 58 59 // TODO: even more jitter-inducing parallelism? 60 61 // int nThreads = ThreadLocalRandom.current().nextInt(1, 50); 62 // ExecutorService pool = Executors.newCachedThreadPool(); 63 // Callable<Void> task = () -> { testInvokeAny_1(); return null; }; 64 // List<Callable<Void>> tasks = Collections.nCopies(nThreads, task); 65 // try { 66 // for (Future<Void> future : pool.invokeAll(tasks)) { 67 // future.get(); 68 // } 69 // } finally { 70 // pool.shutdown(); 71 // } 72 // } 73 74 // public void testInvokeAny_1() throws Exception { 75 76 /** 77 * Test shutdownNow with thread blocked in invokeAny. 78 */ 79 @Test 80 public void testInvokeAny() throws Exception { 81 final int reps = 30_000; 82 ThreadLocalRandom rnd = ThreadLocalRandom.current(); 83 int falseAlarms = 0; 84 for (int rep = 1; rep < reps; rep++) { 85 ForkJoinPool pool = new ForkJoinPool(1); 86 CountDownLatch pleaseShutdownNow = new CountDownLatch(1); 87 int nTasks = rnd.nextInt(2, 5); 88 AtomicInteger threadsStarted = new AtomicInteger(0); 89 AtomicReference<String> poolAtShutdownNow = new AtomicReference<>(); 90 Callable<Void> blockPool = () -> { 91 threadsStarted.getAndIncrement(); 92 // await submission quiescence; may false-alarm 93 // TODO: consider re-checking to reduce false alarms 94 while (threadsStarted.get() + pool.getQueuedSubmissionCount() < nTasks) 95 Thread.yield(); 96 pleaseShutdownNow.countDown(); 97 Thread.sleep(86400_000); 98 return null; 99 }; 100 List<Callable<Void>> tasks = Collections.nCopies(nTasks, blockPool); 101 Runnable shutdown = () -> { 102 try { 103 pleaseShutdownNow.await(); 104 poolAtShutdownNow.set(pool.toString()); 105 pool.shutdownNow(); 106 } catch (Throwable t) { throw new AssertionError(t); } 107 }; 108 Future<Void> shutdownResult = CompletableFuture.runAsync(shutdown); 109 try { 110 try { 111 if (rnd.nextBoolean()) 112 pool.invokeAny(tasks, 10L, SECONDS); 113 else 114 pool.invokeAny(tasks); 115 throw new AssertionError("should throw"); 116 } catch (RejectedExecutionException re) { 117 falseAlarms++; 118 } catch (CancellationException re) { 119 } catch (ExecutionException ex) { 120 Throwable cause = ex.getCause(); 121 if (!(cause instanceof InterruptedException) && 122 !(cause instanceof CancellationException)) 123 throw ex; 124 } catch (TimeoutException ex) { 125 dumpTestThreads(); 126 int i = rep; 127 String detail = String.format( 128 "invokeAny timed out, " 129 + "nTasks=%d rep=%d threadsStarted=%d%n" 130 + "poolAtShutdownNow=%s%n" 131 + "poolAtTimeout=%s%n" 132 + "queuedTaskCount=%d queuedSubmissionCount=%d", 133 nTasks, i, threadsStarted.get(), 134 poolAtShutdownNow, 135 pool, 136 pool.getQueuedTaskCount(), 137 pool.getQueuedSubmissionCount()); 138 throw new AssertionError(detail, ex); 139 } 140 } finally { 141 pool.shutdown(); 142 } 143 if (falseAlarms != 0) 144 System.out.println("Premature shutdowns = " + falseAlarms); 145 shutdownResult.get(); 146 } 147 } 148 149 //--- thread stack dumping (from JSR166TestCase.java) --- 150 151 private static final ThreadMXBean THREAD_MXBEAN 152 = ManagementFactory.getThreadMXBean(); 153 154 /** Returns true if thread info might be useful in a thread dump. */ 155 static boolean threadOfInterest(ThreadInfo info) { 156 final String name = info.getThreadName(); 157 String lockName; 158 if (name == null) 159 return true; 160 if (name.equals("Signal Dispatcher") 161 || name.equals("WedgedTestDetector")) 162 return false; 163 if (name.equals("Reference Handler")) { 164 // Reference Handler stacktrace changed in JDK-8156500 165 StackTraceElement[] stackTrace; String methodName; 166 if ((stackTrace = info.getStackTrace()) != null 167 && stackTrace.length > 0 168 && (methodName = stackTrace[0].getMethodName()) != null 169 && methodName.equals("waitForReferencePendingList")) 170 return false; 171 // jdk8 Reference Handler stacktrace 172 if ((lockName = info.getLockName()) != null 173 && lockName.startsWith("java.lang.ref")) 174 return false; 175 } 176 if ((name.equals("Finalizer") || name.equals("Common-Cleaner")) 177 && (lockName = info.getLockName()) != null 178 && lockName.startsWith("java.lang.ref")) 179 return false; 180 if (name.startsWith("ForkJoinPool.commonPool-worker") 181 && (lockName = info.getLockName()) != null 182 && lockName.startsWith("java.util.concurrent.ForkJoinPool")) 183 return false; 184 return true; 185 } 186 187 /** 188 * A debugging tool to print stack traces of most threads, as jstack does. 189 * Uninteresting threads are filtered out. 190 */ 191 static void dumpTestThreads() { 192 System.err.println("------ stacktrace dump start ------"); 193 for (ThreadInfo info : THREAD_MXBEAN.dumpAllThreads(true, true)) 194 if (threadOfInterest(info)) 195 System.err.print(info); 196 System.err.println("------ stacktrace dump end ------"); 197 } 198 199 }