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