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