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 }