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 the synchronization statistics support: 28 * @author Mandy Chung 29 * @author Jaroslav Bachorik 30 * 31 * @run main/othervm SynchronizationStatistics 32 */ 33 34 import java.lang.management.*; 35 import java.util.concurrent.Phaser; 36 import java.util.function.Supplier; 37 38 public class SynchronizationStatistics { 39 private static class LockerThread extends Thread { 40 public LockerThread(Runnable r) { 41 super(r, "LockerThread"); 42 } 43 } 44 45 private static final ThreadMXBean mbean = ManagementFactory.getThreadMXBean(); 46 47 private static final boolean blockedTimeCheck = 48 mbean.isThreadContentionMonitoringSupported(); 49 50 51 public static void main(String args[]) throws Exception { 52 if (blockedTimeCheck) { 53 mbean.setThreadContentionMonitoringEnabled(true); 54 } 55 56 if (!mbean.isThreadContentionMonitoringEnabled()) { 57 throw new RuntimeException("TEST FAILED: " + 58 "Thread Contention Monitoring is not enabled"); 59 } 60 61 testBlockingOnSimpleMonitor(); 62 testBlockingOnNestedMonitor(); 63 testWaitingOnSimpleMonitor(); 64 testMultiWaitingOnSimpleMonitor(); 65 testWaitingOnNestedMonitor(); 66 67 System.out.println("Test passed."); 68 } 69 70 private static LockerThread newLockerThread(Runnable r) { 71 LockerThread t = new LockerThread(r); 72 t.setDaemon(true); 73 return t; 74 } 75 76 private static void waitForThreadState(Thread t, Thread.State state) throws InterruptedException { 77 while (t.getState() != state) { 78 Thread.sleep(3); 79 } 80 } 81 82 /** 83 * Tests that blocking on a single monitor properly increases the 84 * blocked count at least by 1. Also asserts that the correct lock name is provided. 85 */ 86 private static void testBlockingOnSimpleMonitor() throws Exception { 87 System.out.println("testBlockingOnSimpleMonitor"); 88 final Object lock1 = new Object(); 89 System.out.println("Lock1 = " + lock1); 90 91 final Phaser p = new Phaser(2); 92 LockerThread lt = newLockerThread(new Runnable() { 93 @Override 94 public void run() { 95 p.arriveAndAwaitAdvance(); // phase[1] 96 synchronized(lock1) { 97 System.out.println("[LockerThread obtained Lock1]"); 98 p.arriveAndAwaitAdvance(); // phase[2] 99 } 100 p.arriveAndAwaitAdvance(); // phase[3] 101 } 102 }); 103 104 lt.start(); 105 long tid = lt.getId(); 106 ThreadInfo ti = mbean.getThreadInfo(tid); 107 String lockName = null; 108 synchronized(lock1) { 109 p.arriveAndAwaitAdvance(); // phase[1] 110 waitForThreadState(lt, Thread.State.BLOCKED); 111 do { 112 lockName = mbean.getThreadInfo(tid).getLockName(); 113 } while (lockName == null); 114 } 115 116 p.arriveAndAwaitAdvance(); // phase[2] 117 testBlocked(ti, () -> mbean.getThreadInfo(tid), lockName, lock1); 118 p.arriveAndDeregister(); // phase[3] 119 120 lt.join(); 121 122 printok(); 123 } 124 125 /** 126 * Tests that blocking on a nested monitor properly increases the 127 * blocked count at least by 1 - it is not affected by the nesting depth. 128 * Also asserts that the correct lock name is provided. 129 */ 130 private static void testBlockingOnNestedMonitor() throws Exception { 131 System.out.println("testBlockingOnNestedMonitor"); 132 final Object lock1 = new Object(); 133 final Object lock2 = new Object(); 134 135 System.out.println("Lock1 = " + lock1); 136 System.out.println("Lock2 = " + lock2); 137 138 final Phaser p = new Phaser(2); 139 LockerThread lt = newLockerThread(new Runnable() { 140 @Override 141 public void run() { 142 p.arriveAndAwaitAdvance(); // phase[1] 143 synchronized(lock1) { 144 System.out.println("[LockerThread obtained Lock1]"); 145 p.arriveAndAwaitAdvance(); // phase[2] 146 p.arriveAndAwaitAdvance(); // phase[3] 147 synchronized(lock2) { 148 System.out.println("[LockerThread obtained Lock2]"); 149 p.arriveAndAwaitAdvance(); // phase[4] 150 } 151 p.arriveAndAwaitAdvance(); // phase[5] 152 } 153 } 154 }); 155 156 lt.start(); 157 long tid = lt.getId(); 158 ThreadInfo ti = mbean.getThreadInfo(tid); 159 String lockName = null; 160 synchronized(lock1) { 161 p.arriveAndAwaitAdvance(); // phase[1] 162 waitForThreadState(lt, Thread.State.BLOCKED); 163 do { 164 lockName = mbean.getThreadInfo(tid).getLockName(); 165 } while (lockName == null); 166 } 167 p.arriveAndAwaitAdvance(); // phase[2] 168 169 ti = testBlocked(ti, () -> mbean.getThreadInfo(tid), lockName, lock1); 170 171 synchronized(lock2) { 172 p.arriveAndAwaitAdvance(); // phase [3] 173 waitForThreadState(lt, Thread.State.BLOCKED); 174 do { 175 lockName = mbean.getThreadInfo(tid).getLockName(); 176 } while (lockName == null); 177 } 178 p.arriveAndAwaitAdvance(); // phase [4] 179 testBlocked(ti, () -> mbean.getThreadInfo(tid), lockName, lock2); 180 p.arriveAndDeregister(); 181 182 lt.join(); 183 184 printok(); 185 } 186 187 /** 188 * Tests that waiting on a single monitor properly increases the waited 189 * count by 1 and the waited time by a positive number. 190 */ 191 private static void testWaitingOnSimpleMonitor() throws Exception { 192 System.out.println("testWaitingOnSimpleMonitor"); 193 final Object lock1 = new Object(); 194 final Phaser p = new Phaser(2); 195 LockerThread lt = newLockerThread(new Runnable() { 196 @Override 197 public void run() { 198 p.arriveAndAwaitAdvance(); // phase[1] 199 synchronized(lock1) { 200 System.out.println("[LockerThread obtained Lock1]"); 201 try { 202 lock1.wait(300); 203 } catch (InterruptedException ex) { 204 // ignore 205 } 206 p.arriveAndAwaitAdvance(); // phase[2] 207 } 208 p.arriveAndAwaitAdvance(); // phase[3] 209 } 210 }); 211 212 lt.start(); 213 ThreadInfo ti1 = mbean.getThreadInfo(lt.getId()); 214 synchronized(lock1) { 215 p.arriveAndAwaitAdvance(); // phase[1] 216 waitForThreadState(lt, Thread.State.BLOCKED); 217 } 218 p.arriveAndAwaitAdvance(); // phase[2] 219 220 testWaited(ti1, () -> mbean.getThreadInfo(lt.getId()), 1); 221 p.arriveAndDeregister(); // phase[3] 222 223 lt.join(); 224 225 printok(); 226 } 227 228 /** 229 * Tests that waiting multiple times on the same monitor subsequently 230 * increases the waited count by the number of subsequent calls and the 231 * waited time by a positive number. 232 */ 233 private static void testMultiWaitingOnSimpleMonitor() throws Exception { 234 System.out.println("testWaitingOnMultipleMonitors"); 235 final Object lock1 = new Object(); 236 237 final Phaser p = new Phaser(2); 238 LockerThread lt = newLockerThread(new Runnable() { 239 @Override 240 public void run() { 241 p.arriveAndAwaitAdvance(); // phase[1] 242 synchronized(lock1) { 243 System.out.println("[LockerThread obtained Lock1]"); 244 for (int i = 0; i < 3; i++) { 245 try { 246 lock1.wait(300); 247 } catch (InterruptedException ex) { 248 // ignore 249 } 250 p.arriveAndAwaitAdvance(); // phase[2-4] 251 } 252 } 253 p.arriveAndAwaitAdvance(); // phase[5] 254 } 255 }); 256 257 lt.start(); 258 ThreadInfo ti1 = mbean.getThreadInfo(lt.getId()); 259 synchronized(lock1) { 260 p.arriveAndAwaitAdvance(); //phase[1] 261 waitForThreadState(lt, Thread.State.BLOCKED); 262 } 263 int phase = p.getPhase(); 264 while ((p.arriveAndAwaitAdvance() - phase) < 3); // phase[2-4] 265 266 testWaited(ti1, () -> mbean.getThreadInfo(lt.getId()), 3); 267 p.arriveAndDeregister(); // phase[5] 268 269 lt.join(); 270 271 printok(); 272 } 273 274 /** 275 * Tests that waiting on monitors places in nested synchronized blocks 276 * properly increases the waited count by the number of times the "lock.wait()" 277 * was invoked and the waited time by a positive number. 278 */ 279 private static void testWaitingOnNestedMonitor() throws Exception { 280 System.out.println("testWaitingOnNestedMonitor"); 281 final Object lock1 = new Object(); 282 final Object lock2 = new Object(); 283 final Object lock3 = new Object(); 284 285 final Phaser p = new Phaser(2); 286 LockerThread lt = newLockerThread(new Runnable() { 287 @Override 288 public void run() { 289 p.arriveAndAwaitAdvance(); // phase[1] 290 synchronized(lock1) { 291 System.out.println("[LockerThread obtained Lock1]"); 292 try { 293 lock1.wait(300); 294 } catch (InterruptedException ex) { 295 // ignore 296 } 297 298 p.arriveAndAwaitAdvance(); // phase[2] 299 synchronized(lock2) { 300 System.out.println("[LockerThread obtained Lock2]"); 301 try { 302 lock2.wait(300); 303 } catch (InterruptedException ex) { 304 // ignore 305 } 306 307 p.arriveAndAwaitAdvance(); // phase[3] 308 synchronized(lock3) { 309 System.out.println("[LockerThread obtained Lock3]"); 310 try { 311 lock3.wait(300); 312 } catch (InterruptedException ex) { 313 // ignore 314 } 315 p.arriveAndAwaitAdvance(); // phase[4] 316 } 317 } 318 } 319 p.arriveAndAwaitAdvance(); // phase[5] 320 } 321 }); 322 323 lt.start(); 324 ThreadInfo ti1 = mbean.getThreadInfo(lt.getId()); 325 synchronized(lock1) { 326 p.arriveAndAwaitAdvance(); // phase[1] 327 waitForThreadState(lt, Thread.State.BLOCKED); 328 } 329 330 synchronized(lock2) { 331 p.arriveAndAwaitAdvance(); // phase[2] 332 waitForThreadState(lt, Thread.State.BLOCKED); 333 } 334 335 synchronized(lock3) { 336 p.arriveAndAwaitAdvance(); // phase[3] 337 waitForThreadState(lt, Thread.State.BLOCKED); 338 } 339 340 p.arriveAndAwaitAdvance(); // phase[4] 341 testWaited(ti1, () -> mbean.getThreadInfo(lt.getId()), 3); 342 p.arriveAndDeregister(); // phase[5] 343 344 lt.join(); 345 printok(); 346 } 347 348 private static void printok() { 349 System.out.println("OK\n"); 350 } 351 352 private static void testWaited(ThreadInfo ti1, Supplier<ThreadInfo> ti2, int waited) 353 throws InterruptedException { 354 boolean error; 355 do { 356 error = false; 357 ThreadInfo ti = ti2.get(); 358 long waitCntDiff = ti.getWaitedCount() - ti1.getWaitedCount(); 359 long waitTimeDiff = ti.getWaitedTime() - ti1.getWaitedTime(); 360 if (waitCntDiff < waited) { 361 System.err.println( 362 "Unexpected diff in waited count. Expecting at least " 363 + waited + " , got " + waitCntDiff 364 ); 365 error = true; 366 } 367 if (waitTimeDiff <= 0) { 368 System.err.println( 369 "Unexpected diff in waited time. Expecting increasing " + 370 "value, got " + waitTimeDiff + "ms" 371 ); 372 error = true; 373 } 374 if (error) { 375 System.err.println("Retrying in 20ms ..."); 376 Thread.sleep(20); 377 } 378 } while (error); 379 } 380 381 private static ThreadInfo testBlocked(ThreadInfo ti1, Supplier<ThreadInfo> ti2, 382 String lockName, final Object lock) 383 throws InterruptedException { 384 boolean error; 385 ThreadInfo ti = null; 386 do { 387 error = false; 388 ti = ti2.get(); 389 long blkCntDiff = ti.getBlockedCount() - ti1.getBlockedCount(); 390 long blkTimeDiff = ti.getBlockedTime() - ti1.getBlockedTime(); 391 392 System.out.println("testBlocked: [" + blkCntDiff + ", " + blkTimeDiff + ", " + lockName + "]"); 393 394 if (blkCntDiff < 1) { 395 System.err.println( 396 "Unexpected diff in blocked count. Expecting at least 1, " + 397 "got " + blkCntDiff 398 ); 399 error = true; 400 } 401 if (blkTimeDiff < 0) { 402 System.err.println( 403 "Unexpected diff in blocked time. Expecting a positive " + 404 "number, got " + blkTimeDiff 405 ); 406 error = true; 407 } 408 if (!lockName.equals(lock.toString())) { 409 System.err.println( 410 "Unexpected blocked monitor name. Expecting " + 411 lock.toString() + ", got " + lockName 412 ); 413 error = true; 414 } 415 if (error) { 416 System.err.println("Retrying in 20ms ..."); 417 Thread.sleep(20); 418 } 419 } while (error); 420 return ti; 421 } 422 }