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 }