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     4892507 8020875 8021335
  27  * @summary Basic Test for the following reset methods:
  28  *          - ThreadMXBean.resetPeakThreadCount()
  29  * @author  Mandy Chung
  30  * @author  Jaroslav Bachorik
  31  *
  32  * @build ResetPeakThreadCount
  33  * @build ThreadDump
  34  * @run main/othervm ResetPeakThreadCount
  35  */
  36 
  37 import java.lang.management.*;
  38 
  39 public class ResetPeakThreadCount {
  40     // initial number of new threads started
  41     private static final int DAEMON_THREADS_1 = 8;
  42     private static final int EXPECTED_PEAK_DELTA_1 = 8;
  43 
  44     // Terminate half of the threads started
  45     private static final int TERMINATE_1 = 4;
  46 
  47     // start new threads but expected the peak unchanged
  48     private static final int DAEMON_THREADS_2 = 2;
  49     private static final int EXPECTED_PEAK_DELTA_2 = 0;
  50 
  51     // peak thread count reset before starting new threads
  52     private static final int DAEMON_THREADS_3 = 4;
  53     private static final int EXPECTED_PEAK_DELTA_3 = 4;
  54 
  55     private static final int TERMINATE_2 = 8;
  56 
  57     private static final int TERMINATE_3 = 2;
  58 
  59     private static final int ALL_THREADS = DAEMON_THREADS_1 +
  60         DAEMON_THREADS_2 + DAEMON_THREADS_3;
  61     // barrier for threads communication
  62     private static final Barrier barrier = new Barrier(DAEMON_THREADS_1);
  63 
  64     private static final Thread allThreads[] = new Thread[ALL_THREADS];
  65     private static final boolean live[] = new boolean[ALL_THREADS];
  66     private static final ThreadMXBean mbean = ManagementFactory.getThreadMXBean();
  67     private static volatile boolean testFailed = false;
  68 
  69     public static void main(String[] argv) throws Exception {
  70         // This test does not expect any threads to be created
  71         // by the test harness after main() is invoked.
  72         // The checkThreadCount() method is to produce more
  73         // diagnostic information in case any unexpected test failure occur.
  74         long previous = mbean.getThreadCount();
  75         long current = previous;
  76 
  77         // reset the peak to start from a scratch
  78         resetPeak(current);
  79 
  80         // start DAEMON_THREADS_1 number of threads
  81         current = startThreads(0, DAEMON_THREADS_1, EXPECTED_PEAK_DELTA_1);
  82 
  83         checkThreadCount(previous, current, DAEMON_THREADS_1);
  84         previous = current;
  85 
  86         // terminate TERMINATE_1 number of threads and reset peak
  87         current = terminateThreads(0, TERMINATE_1);
  88 
  89         checkThreadCount(previous, current, TERMINATE_1 * -1);
  90 
  91         previous = current;
  92 
  93         // start DAEMON_THREADS_2 number of threads
  94         // expected peak is unchanged
  95         current = startThreads(DAEMON_THREADS_1, DAEMON_THREADS_2,
  96                                EXPECTED_PEAK_DELTA_2);
  97 
  98         checkThreadCount(previous, current, DAEMON_THREADS_2);
  99         previous = current;
 100 
 101         // Reset the peak
 102         resetPeak(current);
 103 
 104         // start DAEMON_THREADS_3 number of threads
 105         current = startThreads(DAEMON_THREADS_1 + DAEMON_THREADS_2,
 106                                DAEMON_THREADS_3, EXPECTED_PEAK_DELTA_3);
 107 
 108         checkThreadCount(previous, current, DAEMON_THREADS_3);
 109         previous = current;
 110 
 111         // terminate TERMINATE_2 number of threads and reset peak
 112         current = terminateThreads(TERMINATE_1, TERMINATE_2);
 113 
 114         checkThreadCount(previous, current, TERMINATE_2 * -1);
 115         previous = current;
 116 
 117         resetPeak(current);
 118 
 119         // terminate TERMINATE_3 number of threads and reset peak
 120         current = terminateThreads(TERMINATE_1 + TERMINATE_2, TERMINATE_3);
 121 
 122         checkThreadCount(previous, current, TERMINATE_3 * -1);
 123         resetPeak(current);
 124 
 125         if (testFailed)
 126             throw new RuntimeException("TEST FAILED.");
 127 
 128         System.out.println("Test passed");
 129     }
 130 
 131     private static long startThreads(int from, int count, int delta) throws InterruptedException {
 132         // get current peak thread count
 133         long peak1 = mbean.getPeakThreadCount();
 134         long current = mbean.getThreadCount();
 135 
 136         // Start threads and wait to be sure they all are alive
 137         System.out.println("Starting " + count + " threads....");
 138         barrier.set(count);
 139         synchronized(live) {
 140             for (int i = from; i < (from + count); i++) {
 141                 live[i] = true;
 142                 allThreads[i] = new MyThread(i);
 143                 allThreads[i].setDaemon(true);
 144                 allThreads[i].start();
 145             }
 146         }
 147         // wait until all threads have started.
 148         barrier.await();
 149 
 150         // get peak thread count after daemon threads have started
 151         long peak2 = mbean.getPeakThreadCount();
 152 
 153         System.out.println("   Current = " + mbean.getThreadCount() +
 154             " Peak before = " + peak1 + " after: " + peak2);
 155 
 156         if (peak2 != (peak1 + delta)) {
 157             throw new RuntimeException("Current Peak = " + peak2 +
 158                 " Expected to be == previous peak = " + peak1 + " + " +
 159                 delta);
 160         }
 161         // wait until the current thread count gets incremented
 162         while (mbean.getThreadCount() < (current + count)) {
 163             Thread.sleep(100);
 164         }
 165         current = mbean.getThreadCount();
 166         System.out.println("   Live thread count before returns " + current);
 167         return current;
 168     }
 169 
 170     private static long terminateThreads(int from, int count) throws InterruptedException {
 171         // get current peak thread count
 172         long peak1 = mbean.getPeakThreadCount();
 173 
 174         // Stop daemon threads and wait to be sure they all are dead
 175         System.out.println("Terminating " + count + " threads....");
 176         barrier.set(count);
 177         synchronized(live) {
 178             for (int i = from; i < (from+count); i++) {
 179                 live[i] = false;
 180             }
 181             live.notifyAll();
 182         }
 183         // wait until daemon threads terminated.
 184         barrier.await();
 185 
 186         // get peak thread count after daemon threads have terminated
 187         long peak2 = mbean.getPeakThreadCount();
 188         // assuming no system thread is added
 189         if (peak2 != peak1) {
 190             throw new RuntimeException("Current Peak = " + peak2 +
 191                 " Expected to be = previous peak = " + peak1);
 192         }
 193 
 194         for (int i = from; i < (from+count); i++) {
 195             allThreads[i].join();
 196         }
 197 
 198         // there is a race in the counter update logic somewhere causing
 199         // the thread counters go ff
 200         // we need to give the terminated threads some extra time to really die
 201         // JDK-8021335
 202         Thread.sleep(500);
 203 
 204         long current = mbean.getThreadCount();
 205         System.out.println("   Live thread count before returns " + current);
 206         return current;
 207     }
 208 
 209     private static void resetPeak(long expectedCount) {
 210         long peak3 = mbean.getPeakThreadCount();
 211         long current = mbean.getThreadCount();
 212 
 213         // Nightly testing showed some intermittent failure.
 214         // Check here to get diagnostic information if some strange
 215         // behavior occurs.
 216         checkThreadCount(expectedCount, current, 0);
 217 
 218         // Reset peak thread count
 219         mbean.resetPeakThreadCount();
 220 
 221         long afterResetPeak = mbean.getPeakThreadCount();
 222         long afterResetCurrent = mbean.getThreadCount();
 223         System.out.println("Reset peak before = " + peak3 +
 224             " current = " + current +
 225             " after reset peak = " + afterResetPeak +
 226             " current = " + afterResetCurrent);
 227 
 228         if (afterResetPeak != current) {
 229             throw new RuntimeException("Current Peak after reset = " +
 230                 afterResetPeak +
 231                 " Expected to be = current count = " + current);
 232         }
 233     }
 234 
 235     private static void checkThreadCount(long previous, long current, int expectedDelta) {
 236         if (current != previous + expectedDelta) {
 237             ThreadDump.threadDump();
 238             throw new RuntimeException("***** Unexpected thread count:" +
 239                                " previous = " + previous +
 240                                " current = " + current +
 241                                " delta = " + expectedDelta + "*****");
 242         }
 243     }
 244 
 245     // The MyThread thread lives as long as correspondent live[i] value is true
 246     private static class MyThread extends Thread {
 247         int id;
 248 
 249         MyThread(int id) {
 250             this.id = id;
 251         }
 252 
 253         public void run() {
 254             // signal started
 255             barrier.signal();
 256             synchronized(live) {
 257                 while (live[id]) {
 258                     try {
 259                         live.wait(100);
 260                     } catch (InterruptedException e) {
 261                         System.out.println("Unexpected exception is thrown.");
 262                         e.printStackTrace(System.out);
 263                         testFailed = true;
 264                     }
 265                 }
 266             }
 267             // signal about to exit
 268             barrier.signal();
 269         }
 270     }
 271 
 272 }