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