1 /*
   2  * Copyright (c) 2006, 2012, 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     6467152 6716076 6829503
  27  * @summary deadlock occurs in LogManager initialization and JVM termination
  28  * @author  Serguei Spitsyn / Hitachi / Martin Buchholz
  29  *
  30  * @build    LoggingDeadlock2
  31  * @run  main LoggingDeadlock2
  32  * @key randomness
  33  */
  34 
  35 /*
  36  *
  37  * There is a clear deadlock between LogManager.<clinit> and
  38  * Cleaner.run() methods.
  39  * T1 thread:
  40  *   The LogManager.<clinit> creates LogManager.manager object,
  41  *   sets shutdown hook with the Cleaner class and then waits
  42  *   to lock the LogManager.manager monitor.
  43  * T2 thread:
  44  *   It is started by the System.exit() as shutdown hook thread.
  45  *   It locks the LogManager.manager monitor and then calls the
  46  *   static methods of the LogManager class (in this particular
  47  *   case it is a trick of the inner classes implementation).
  48  *   It is waits when the LogManager.<clinit> is completed.
  49  *
  50  * This is a regression test for this bug.
  51  */
  52 
  53 import java.util.Arrays;
  54 import java.util.List;
  55 import java.util.Random;
  56 import java.util.concurrent.CyclicBarrier;
  57 import java.util.concurrent.atomic.AtomicInteger;
  58 import java.util.logging.LogManager;
  59 import java.io.File;
  60 import java.io.InputStream;
  61 import java.io.InputStreamReader;
  62 import java.io.Reader;
  63 
  64 public class LoggingDeadlock2 {
  65 
  66     public static void realMain(String arg[]) throws Throwable {
  67         try {
  68             System.out.println(javaChildArgs);
  69             ProcessBuilder pb = new ProcessBuilder(javaChildArgs);
  70             ProcessResults r = run(pb.start());
  71             equal(r.exitValue(), 99);
  72             equal(r.out(), "");
  73             equal(r.err(), "");
  74         } catch (Throwable t) { unexpected(t); }
  75     }
  76 
  77     public static class JavaChild {
  78         public static void main(String args[]) throws Throwable {
  79             final CyclicBarrier startingGate = new CyclicBarrier(2);
  80             final Throwable[] thrown = new Throwable[1];
  81 
  82             // Some random variation, to help tickle races.
  83             final Random rnd = new Random();
  84             final boolean dojoin = rnd.nextBoolean();
  85             final int JITTER = 1024;
  86             final int iters1 = rnd.nextInt(JITTER);
  87             final int iters2 = JITTER - iters1;
  88             final AtomicInteger counter = new AtomicInteger(0);
  89 
  90             Thread exiter = new Thread() {
  91                 public void run() {
  92                     try {
  93                         startingGate.await();
  94                         for (int i = 0; i < iters1; i++)
  95                             counter.getAndIncrement();
  96                         System.exit(99);
  97                     } catch (Throwable t) {
  98                         t.printStackTrace();
  99                         System.exit(86);
 100                     }
 101                 }};
 102             exiter.start();
 103 
 104             startingGate.await();
 105             for (int i = 0; i < iters2; i++)
 106                 counter.getAndIncrement();
 107             // This may or may not result in a first call to
 108             // Runtime.addShutdownHook after shutdown has already
 109             // commenced.
 110             LogManager.getLogManager();
 111 
 112             if (dojoin) {
 113                 exiter.join();
 114                 if (thrown[0] != null)
 115                     throw new Error(thrown[0]);
 116                 check(counter.get() == JITTER);
 117             }
 118         }
 119     }
 120 
 121     //----------------------------------------------------------------
 122     // The rest of this test is copied from ProcessBuilder/Basic.java
 123     //----------------------------------------------------------------
 124     private static final String javaExe =
 125         System.getProperty("java.home") +
 126         File.separator + "bin" + File.separator + "java";
 127 
 128     private static final String classpath =
 129         System.getProperty("java.class.path");
 130 
 131     private static final List<String> javaChildArgs =
 132         Arrays.asList(new String[]
 133             { javaExe, "-classpath", classpath,
 134               "LoggingDeadlock2$JavaChild"});
 135 
 136     private static class ProcessResults {
 137         private final String out;
 138         private final String err;
 139         private final int exitValue;
 140         private final Throwable throwable;
 141 
 142         public ProcessResults(String out,
 143                               String err,
 144                               int exitValue,
 145                               Throwable throwable) {
 146             this.out = out;
 147             this.err = err;
 148             this.exitValue = exitValue;
 149             this.throwable = throwable;
 150         }
 151 
 152         public String out()          { return out; }
 153         public String err()          { return err; }
 154         public int exitValue()       { return exitValue; }
 155 
 156         public String toString() {
 157             StringBuilder sb = new StringBuilder();
 158             sb.append("<STDOUT>\n" + out() + "</STDOUT>\n")
 159                 .append("<STDERR>\n" + err() + "</STDERR>\n")
 160                 .append("exitValue = " + exitValue + "\n");
 161             if (throwable != null)
 162                 sb.append(throwable.getStackTrace());
 163             return sb.toString();
 164         }
 165     }
 166 
 167     private static class StreamAccumulator extends Thread {
 168         private final InputStream is;
 169         private final StringBuilder sb = new StringBuilder();
 170         private Throwable throwable = null;
 171 
 172         public String result () throws Throwable {
 173             if (throwable != null)
 174                 throw throwable;
 175             return sb.toString();
 176         }
 177 
 178         StreamAccumulator (InputStream is) {
 179             this.is = is;
 180         }
 181 
 182         public void run() {
 183             try {
 184                 Reader r = new InputStreamReader(is);
 185                 char[] buf = new char[4096];
 186                 int n;
 187                 while ((n = r.read(buf)) > 0) {
 188                     sb.append(buf,0,n);
 189                 }
 190             } catch (Throwable t) {
 191                 throwable = t;
 192             } finally {
 193                 try { is.close(); }
 194                 catch (Throwable t) { throwable = t; }
 195             }
 196         }
 197     }
 198 
 199     private static ProcessResults run(Process p) {
 200         Throwable throwable = null;
 201         int exitValue = -1;
 202         String out = "";
 203         String err = "";
 204 
 205         StreamAccumulator outAccumulator =
 206             new StreamAccumulator(p.getInputStream());
 207         StreamAccumulator errAccumulator =
 208             new StreamAccumulator(p.getErrorStream());
 209 
 210         try {
 211             outAccumulator.start();
 212             errAccumulator.start();
 213 
 214             exitValue = p.waitFor();
 215 
 216             outAccumulator.join();
 217             errAccumulator.join();
 218 
 219             out = outAccumulator.result();
 220             err = errAccumulator.result();
 221         } catch (Throwable t) {
 222             throwable = t;
 223         }
 224 
 225         return new ProcessResults(out, err, exitValue, throwable);
 226     }
 227 
 228     //--------------------- Infrastructure ---------------------------
 229     static volatile int passed = 0, failed = 0;
 230     static void pass() {passed++;}
 231     static void fail() {failed++; Thread.dumpStack();}
 232     static void fail(String msg) {System.out.println(msg); fail();}
 233     static void unexpected(Throwable t) {failed++; t.printStackTrace();}
 234     static void check(boolean cond) {if (cond) pass(); else fail();}
 235     static void check(boolean cond, String m) {if (cond) pass(); else fail(m);}
 236     static void equal(Object x, Object y) {
 237         if (x == null ? y == null : x.equals(y)) pass();
 238         else fail(x + " not equal to " + y);}
 239     public static void main(String[] args) throws Throwable {
 240         try {realMain(args);} catch (Throwable t) {unexpected(t);}
 241         System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
 242         if (failed > 0) throw new AssertionError("Some tests failed");}
 243 }