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