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