1 /* 2 * Copyright (c) 2003, 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 * @bug 4530538 27 * @key intermittent 28 * @summary Basic unit test of ThreadMXBean.getAllThreadIds() 29 * @author Alexei Guibadoulline and Mandy Chung 30 * 31 * @run main/othervm AllThreadIds 32 */ 33 34 import java.lang.management.*; 35 import java.time.Instant; 36 import java.util.concurrent.Phaser; 37 import java.util.function.Supplier; 38 39 public class AllThreadIds { 40 /** 41 * A supplier wrapper for the delayed format printing. 42 * The supplied value will have to be formatted as <em>$s</em> 43 * @param <T> The wrapped type 44 */ 45 private static final class ArgWrapper<T> { 46 private final Supplier<T> val; 47 48 public ArgWrapper(Supplier<T> val) { 49 this.val = val; 50 } 51 52 @Override 53 public String toString() { 54 T resolved = val.get(); 55 return resolved != null ? resolved.toString() : null; 56 } 57 } 58 59 static final int DAEMON_THREADS = 20; 60 static final int USER_THREADS = 5; 61 static final int ALL_THREADS = DAEMON_THREADS + USER_THREADS; 62 private static final boolean live[] = new boolean[ALL_THREADS]; 63 private static final Thread allThreads[] = new Thread[ALL_THREADS]; 64 private static final ThreadMXBean mbean = ManagementFactory.getThreadMXBean(); 65 private static boolean testFailed = false; 66 private static boolean trace = false; 67 68 private static long prevTotalThreadCount = 0; 69 private static int prevLiveThreadCount = 0; 70 private static int prevPeakThreadCount = 0; 71 72 private static final Phaser startupCheck = new Phaser(ALL_THREADS + 1); 73 74 private static void printThreadList() { 75 long[] list = mbean.getAllThreadIds(); 76 for (int i = 1; i <= list.length; i++) { 77 System.out.println(i + ": Thread id = " + list[i-1]); 78 } 79 for (int i = 0; i < ALL_THREADS; i++) { 80 Thread t = allThreads[i]; 81 System.out.println(t.getName() + " Id = " + t.getId() + 82 " die = " + live[i] + 83 " alive = " + t.isAlive()); 84 } 85 } 86 87 private static void checkInitialState() throws Exception { 88 updateCounters(); 89 checkThreadCount(0, 0); 90 } 91 92 private static void checkAllThreadsAlive() throws Exception { 93 updateCounters(); 94 95 // Start all threads and wait to be sure they all are alive 96 for (int i = 0; i < ALL_THREADS; i++) { 97 setLive(i, true); 98 allThreads[i] = new MyThread(i); 99 allThreads[i].setDaemon(i < DAEMON_THREADS); 100 allThreads[i].start(); 101 } 102 // wait until all threads are started. 103 startupCheck.arriveAndAwaitAdvance(); 104 105 checkThreadCount(ALL_THREADS, 0); 106 if (trace) { 107 printThreadList(); 108 } 109 // Check mbean now. All threads must appear in getAllThreadIds() list 110 long[] list = mbean.getAllThreadIds(); 111 112 for (int i = 0; i < ALL_THREADS; i++) { 113 long expectedId = allThreads[i].getId(); 114 boolean found = false; 115 116 if (trace) { 117 System.out.print("Looking for thread with id " + expectedId); 118 } 119 for (int j = 0; j < list.length; j++) { 120 if (expectedId == list[j]) { 121 found = true; 122 break; 123 } 124 } 125 126 if (!found) { 127 testFailed = true; 128 } 129 if (trace) { 130 if (!found) { 131 System.out.print(". TEST FAILED."); 132 } 133 System.out.println(); 134 } 135 } 136 if (trace) { 137 System.out.println(); 138 } 139 } 140 141 private static void checkDaemonThreadsDead() throws Exception { 142 updateCounters(); 143 144 // Stop daemon threads, wait to be sure they all are dead, and check 145 // that they disappeared from getAllThreadIds() list 146 for (int i = 0; i < DAEMON_THREADS; i++) { 147 setLive(i, false); 148 } 149 150 // make sure the daemon threads are completely dead 151 joinDaemonThreads(); 152 153 // and check the reported thread count 154 checkThreadCount(0, DAEMON_THREADS); 155 156 // Check mbean now 157 long[] list = mbean.getAllThreadIds(); 158 159 for (int i = 0; i < ALL_THREADS; i++) { 160 long expectedId = allThreads[i].getId(); 161 boolean found = false; 162 boolean alive = (i >= DAEMON_THREADS); 163 164 if (trace) { 165 System.out.print("Looking for thread with id " + expectedId + 166 (alive ? " expected alive." : " expected terminated.")); 167 } 168 for (int j = 0; j < list.length; j++) { 169 if (expectedId == list[j]) { 170 found = true; 171 break; 172 } 173 } 174 175 if (alive != found) { 176 testFailed = true; 177 } 178 if (trace) { 179 if (alive != found) { 180 System.out.println(" TEST FAILED."); 181 } else { 182 System.out.println(); 183 } 184 } 185 } 186 } 187 188 private static void checkAllThreadsDead() throws Exception { 189 updateCounters(); 190 191 // Stop all threads and wait to be sure they all are dead 192 for (int i = DAEMON_THREADS; i < ALL_THREADS; i++) { 193 setLive(i, false); 194 } 195 196 // make sure the non-daemon threads are completely dead 197 joinNonDaemonThreads(); 198 199 // and check the thread count 200 checkThreadCount(0, ALL_THREADS - DAEMON_THREADS); 201 } 202 203 private static void checkThreadCount(int numNewThreads, 204 int numTerminatedThreads) 205 throws Exception { 206 207 checkLiveThreads(numNewThreads, numTerminatedThreads); 208 checkPeakThreads(numNewThreads); 209 checkTotalThreads(numNewThreads); 210 checkThreadIds(); 211 } 212 213 private static void checkLiveThreads(int numNewThreads, 214 int numTerminatedThreads) 215 throws InterruptedException { 216 int diff = numNewThreads - numTerminatedThreads; 217 218 waitTillEquals( 219 diff + prevLiveThreadCount, 220 ()->(long)mbean.getThreadCount(), 221 "Unexpected number of live threads: " + 222 " Prev live = %1$d Current live = ${provided} Threads added = %2$d" + 223 " Threads terminated = %3$d", 224 ()->prevLiveThreadCount, 225 ()->numNewThreads, 226 ()->numTerminatedThreads 227 ); 228 } 229 230 private static void checkPeakThreads(int numNewThreads) 231 throws InterruptedException { 232 233 waitTillEquals(numNewThreads + prevPeakThreadCount, 234 ()->(long)mbean.getPeakThreadCount(), 235 "Unexpected number of peak threads: " + 236 " Prev peak = %1$d Current peak = ${provided} Threads added = %2$d", 237 ()->prevPeakThreadCount, 238 ()->numNewThreads 239 ); 240 } 241 242 private static void checkTotalThreads(int numNewThreads) 243 throws InterruptedException { 244 245 waitTillEquals(numNewThreads + prevTotalThreadCount, 246 ()->mbean.getTotalStartedThreadCount(), 247 "Unexpected number of total threads: " + 248 " Prev Total = %1$d Current Total = ${provided} Threads added = %2$d", 249 ()->prevTotalThreadCount, 250 ()->numNewThreads 251 ); 252 } 253 254 private static void checkThreadIds() throws InterruptedException { 255 long[] list = mbean.getAllThreadIds(); 256 257 waitTillEquals( 258 list.length, 259 ()->(long)mbean.getThreadCount(), 260 "Array length returned by " + 261 "getAllThreadIds() = %1$d not matched count = ${provided}", 262 ()->list.length 263 ); 264 } 265 266 /** 267 * Waits till the <em>expectedVal</em> equals to the <em>retrievedVal</em>. 268 * It will report a status message on the first occasion of the value mismatch 269 * and then, subsequently, when the <em>retrievedVal</em> value changes. 270 * @param expectedVal The value to wait for 271 * @param retrievedVal The supplier of the value to check against the <em>expectedVal</em> 272 * @param msgFormat The formatted message to be printed in case of mismatch 273 * @param msgArgs The parameters to the formatted message 274 * @throws InterruptedException 275 */ 276 private static void waitTillEquals(long expectedVal, Supplier<Long> retrievedVal, 277 String msgFormat, Supplier<Object> ... msgArgs) 278 throws InterruptedException { 279 Object[] args = null; 280 281 long countPrev = -1; 282 while (true) { 283 Long count = retrievedVal.get(); 284 if (count == expectedVal) break; 285 if (countPrev == -1 || countPrev != count) { 286 if (args == null) { 287 args = new Object[msgArgs.length]; 288 for(int i=0; i < msgArgs.length; i++) { 289 args[i] = new ArgWrapper<>((Supplier<Object>)msgArgs[i]); 290 } 291 } 292 System.err.format("TS: %s\n", Instant.now()); 293 System.err.format( 294 msgFormat 295 .replace("${provided}", String.valueOf(count)) 296 .replace("$d", "$s"), 297 args 298 ).flush(); 299 printThreadList(); 300 System.err.println("\nRetrying ...\n"); 301 } 302 countPrev = count; 303 Thread.sleep(1); 304 } 305 } 306 307 private static void updateCounters() { 308 prevTotalThreadCount = mbean.getTotalStartedThreadCount(); 309 prevLiveThreadCount = mbean.getThreadCount(); 310 prevPeakThreadCount = mbean.getPeakThreadCount(); 311 } 312 313 public static void main(String args[]) throws Exception { 314 if (args.length > 0 && args[0].equals("trace")) { 315 trace = true; 316 } 317 318 checkInitialState(); 319 checkAllThreadsAlive(); 320 checkDaemonThreadsDead(); 321 checkAllThreadsDead(); 322 323 if (testFailed) 324 throw new RuntimeException("TEST FAILED."); 325 326 System.out.println("Test passed."); 327 } 328 329 private static void joinDaemonThreads() throws InterruptedException { 330 for (int i = 0; i < DAEMON_THREADS; i++) { 331 allThreads[i].join(); 332 } 333 } 334 335 private static void joinNonDaemonThreads() throws InterruptedException { 336 for (int i = DAEMON_THREADS; i < ALL_THREADS; i++) { 337 allThreads[i].join(); 338 } 339 } 340 341 private static void setLive(int i, boolean val) { 342 synchronized(live) { 343 live[i] = val; 344 } 345 } 346 347 private static boolean isLive(int i) { 348 synchronized(live) { 349 return live[i]; 350 } 351 } 352 353 // The MyThread thread lives as long as correspondent live[i] value is true 354 private static class MyThread extends Thread { 355 int id; 356 357 MyThread(int id) { 358 this.id = id; 359 } 360 361 public void run() { 362 // signal started 363 startupCheck.arrive(); 364 while (isLive(id)) { 365 try { 366 sleep(100); 367 } catch (InterruptedException e) { 368 System.out.println("Unexpected exception is thrown."); 369 e.printStackTrace(System.out); 370 testFailed = true; 371 } 372 } 373 } 374 } 375 }