1 /*
   2  * Copyright (c) 2001, 2020, 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 package nsk.share;
  25 
  26 /**
  27  * Terminator is used to terminate a stress test with PASS exit status
  28  * before the test is terminated as timed out (and so failed).
  29  *
  30  * <p>Terminator class holds a thread which sleeps for the given amount
  31  * of time, and then wakes up and executes <tt>System.exit()</tt>
  32  * with the given exit status. That thread is daemon, so it doesn't
  33  * prevent application from exiting once all its threads finish
  34  * before it's time for termination. Appointing terminator in zero
  35  * delay implies immediate <tt>exit()</tt>.
  36  *
  37  * <p>There is a limitation: you may appoint no more than one terminator
  38  * per application.
  39  */
  40 public class Terminator {
  41     /**
  42      * Use specific <tt>appoint()</tt> method to appoint terminator.
  43      *
  44      * @see #appoint(int)
  45      * @see #appoint(int,int)
  46      */
  47     protected Terminator() {}
  48 
  49     /**
  50      * One terminator per application, or <tt>null</tt> (by default).
  51      */
  52     private static Thread terminator = null;
  53 
  54     /**
  55      * <p>Return timeout (or waittime) value munus the margin
  56      * value (which is assumed 1 minute by default).
  57      *
  58      * <p>Treat <tt>args[0]</tt> as <tt>$TIMEOUT</tt> value, or seek for
  59      * <tt>-waittime=$WAITTIME</tt> value. If both parameters
  60      * (or either none of them) are assigned, throw an exception to
  61      * report parameters inconsistency.
  62      *
  63      * <p>Also, seek for <tt>-margin=...</tt> assignment, or assume margin
  64      * is 1 minute.
  65      *
  66      * @param args Is usually obtained via the application's command-line.
  67      *
  68      * @throws IllegalArgumentException If <tt>args[]</tt> is inconsistent.
  69      *
  70      * @see #appoint(int)
  71      * @see #appoint(int,int)
  72      */
  73     public static int parseAppointment(String args[]) {
  74         int timeout=-1, margin=1;
  75         int timeouts=0, waittimes=0, margins=0;
  76         for (int i=0; i<args.length; i++) {
  77             if (args[i].startsWith("-")) {
  78                 if (args[i].startsWith("-waittime=")) {
  79                     timeout = Integer.parseInt(args[i].substring(10));
  80                     waittimes++;
  81                 }
  82                 if (args[i].startsWith("-margin=")) {
  83                     margin = Integer.parseInt(args[i].substring(8));
  84                     margins++;
  85                 }
  86             } else {
  87                 if (i == 0) {
  88                     timeout = Integer.parseInt(args[i]);
  89                     timeouts++;
  90                 }
  91             }
  92         };
  93         if (timeouts==0 && waittimes==0)
  94             throw new IllegalArgumentException(
  95                 "no $TIMEOUT, nor -waittime=$WAITTIME is set");
  96         if (waittimes > 1)
  97             throw new IllegalArgumentException(
  98                 "more than one -waittime=... is set");
  99         if (margins > 1)
 100             throw new IllegalArgumentException(
 101                 "more than one -margin=... is set");
 102 
 103         int result = timeout - margin;
 104         if (result <= 0)
 105             throw new IllegalArgumentException(
 106                 "delay appointment must be greater than "+margin+" minutes");
 107         return result;
 108     }
 109 
 110     /**
 111      * Appoint terminator after the given amount of <tt>minutes</tt>,
 112      * so that exit status would be 95 (to simulate JCK-like PASS
 113      * status).
 114      *
 115      * @throws IllegalStateException If terminator is already appointed.
 116      *
 117      * @see #appoint(int,int)
 118      * @see #parseAppointment(String[])
 119      */
 120     public static void appoint(int minutes) {
 121         appoint(minutes,95); // JCK-like PASS status
 122     }
 123 
 124     /**
 125      * Appoint Terminator for the given amount of <tt>minutes</tt>,
 126      * so that the given <tt>status</tt> would be exited when time
 127      * is over.
 128      *
 129      * @throws IllegalStateException If terminator is already appointed.
 130      *
 131      * @see #appoint(int)
 132      * @see #parseAppointment(String[])
 133      */
 134     public static void appoint(int minutes, int status) {
 135         if (terminator != null)
 136             throw new IllegalStateException("Terminator is already appointed.");
 137 
 138         final long timeToExit = System.currentTimeMillis() + 60*1000L*minutes;
 139         final int  exitStatus = status;
 140 
 141         terminator = new Thread(Terminator.class.getName()) {
 142             public void run() {
 143                 long timeToSleep = timeToExit - System.currentTimeMillis();
 144                 if (timeToSleep > 0)
 145                     try {
 146                         //
 147                         // Use wait() instead of sleep(), because Java 2
 148                         // specification doesn't guarantee the method
 149                         // sleep() to yield to other threads.
 150                         //
 151                         Object someDummyObject = new Object();
 152                         synchronized (someDummyObject) {
 153                             someDummyObject.wait(timeToSleep);
 154                         }
 155                     } catch (InterruptedException exception) {
 156                         exception.printStackTrace(System.err);
 157                        //
 158                        // OOPS, the dagger for terminator looks broken:
 159                        //
 160                        return;
 161                     };
 162                 //
 163                 // OK, lets do it now:
 164                 //
 165                 System.err.println(
 166                     "#\n# Terminator: prescheduled program termination.\n#");
 167                 System.exit(exitStatus); // terminator to all threads
 168             }
 169         };
 170 
 171         terminator.setPriority(Thread.MAX_PRIORITY);
 172         terminator.setDaemon(true);
 173         terminator.start();
 174     }
 175 }