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         current = mbean.getThreadCount();
 162         System.out.println("   Live thread count before returns " + current);
 163         return current;
 164     }
 165 
 166     private static long terminateThreads(int from, int count) throws InterruptedException {
 167         // get current peak thread count
 168         long peak1 = mbean.getPeakThreadCount();
 169 
 170         // Stop daemon threads and wait to be sure they all are dead
 171         System.out.println("Terminating " + count + " threads....");
 172         barrier.set(count);
 173         synchronized(live) {
 174             for (int i = from; i < (from+count); i++) {
 175                 live[i] = false;
 176             }
 177             live.notifyAll();
 178         }
 179         // wait until daemon threads terminated.
 180         barrier.await();
 181 
 182         // get peak thread count after daemon threads have terminated
 183         long peak2 = mbean.getPeakThreadCount();
 184         // assuming no system thread is added
 185         if (peak2 != peak1) {
 186             throw new RuntimeException("Current Peak = " + peak2 +
 187                 " Expected to be = previous peak = " + peak1);
 188         }
 189 
 190         for (int i = from; i < (from+count); i++) {
 191             allThreads[i].join();
 192         }
 193 
 194         long current = mbean.getThreadCount();
 195         System.out.println("   Live thread count before returns " + current);
 196         return current;
 197     }
 198 
 199     private static void resetPeak(long expectedCount) {
 200         long peak3 = mbean.getPeakThreadCount();
 201         long current = mbean.getThreadCount();
 202 
 203         // Nightly testing showed some intermittent failure.
 204         // Check here to get diagnostic information if some strange
 205         // behavior occurs.
 206         checkThreadCount(expectedCount, current, 0);
 207 
 208         // Reset peak thread count
 209         mbean.resetPeakThreadCount();
 210 
 211         long afterResetPeak = mbean.getPeakThreadCount();
 212         long afterResetCurrent = mbean.getThreadCount();
 213         System.out.println("Reset peak before = " + peak3 +
 214             " current = " + current +
 215             " after reset peak = " + afterResetPeak +
 216             " current = " + afterResetCurrent);
 217 
 218         if (afterResetPeak != current) {
 219             throw new RuntimeException("Current Peak after reset = " +
 220                 afterResetPeak +
 221                 " Expected to be = current count = " + current);
 222         }
 223     }
 224 
 225     private static void checkThreadCount(long previous, long current, int expectedDelta) {
 226         if (current != previous + expectedDelta) {
 227             ThreadDump.threadDump();
 228             throw new RuntimeException("***** Unexpected thread count:" +
 229                                " previous = " + previous +
 230                                " current = " + current +
 231                                " delta = " + expectedDelta + "*****");
 232         }
 233     }
 234 
 235     // The MyThread thread lives as long as correspondent live[i] value is true
 236     private static class MyThread extends Thread {
 237         int id;
 238 
 239         MyThread(int id) {
 240             this.id = id;
 241         }
 242 
 243         public void run() {
 244             // signal started
 245             barrier.signal();
 246             synchronized(live) {
 247                 while (live[id]) {
 248                     try {
 249                         live.wait(100);
 250                     } catch (InterruptedException e) {
 251                         System.out.println("Unexpected exception is thrown.");
 252                         e.printStackTrace(System.out);
 253                         testFailed = true;
 254                     }
 255                 }
 256             }
 257             // signal about to exit
 258             barrier.signal();
 259         }
 260     }
 261 
 262 }