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 }