1 /* 2 * Copyright (c) 2006, 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/timeout=15 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 log = 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 public Throwable throwable() { return throwable; } 152 153 public String toString() { 154 StringBuilder sb = new StringBuilder(); 155 sb.append("<STDOUT>\n" + out() + "</STDOUT>\n") 156 .append("<STDERR>\n" + err() + "</STDERR>\n") 157 .append("exitValue = " + exitValue + "\n"); 158 if (throwable != null) 159 sb.append(throwable.getStackTrace()); 160 return sb.toString(); 161 } 162 } 163 164 private static class StreamAccumulator extends Thread { 165 private final InputStream is; 166 private final StringBuilder sb = new StringBuilder(); 167 private Throwable throwable = null; 168 169 public String result () throws Throwable { 170 if (throwable != null) 171 throw throwable; 172 return sb.toString(); 173 } 174 175 StreamAccumulator (InputStream is) { 176 this.is = is; 177 } 178 179 public void run() { 180 try { 181 Reader r = new InputStreamReader(is); 182 char[] buf = new char[4096]; 183 int n; 184 while ((n = r.read(buf)) > 0) { 185 sb.append(buf,0,n); 186 } 187 } catch (Throwable t) { 188 throwable = t; 189 } finally { 190 try { is.close(); } 191 catch (Throwable t) { throwable = t; } 192 } 193 } 194 } 195 196 private static ProcessResults run(Process p) { 197 Throwable throwable = null; 198 int exitValue = -1; 199 String out = ""; 200 String err = ""; 201 202 StreamAccumulator outAccumulator = 203 new StreamAccumulator(p.getInputStream()); 204 StreamAccumulator errAccumulator = 205 new StreamAccumulator(p.getErrorStream()); 206 207 try { 208 outAccumulator.start(); 209 errAccumulator.start(); 210 211 exitValue = p.waitFor(); 212 213 outAccumulator.join(); 214 errAccumulator.join(); 215 216 out = outAccumulator.result(); 217 err = errAccumulator.result(); 218 } catch (Throwable t) { 219 throwable = t; 220 } 221 222 return new ProcessResults(out, err, exitValue, throwable); 223 } 224 225 //--------------------- Infrastructure --------------------------- 226 static volatile int passed = 0, failed = 0; 227 static void pass() {passed++;} 228 static void fail() {failed++; Thread.dumpStack();} 229 static void fail(String msg) {System.out.println(msg); fail();} 230 static void unexpected(Throwable t) {failed++; t.printStackTrace();} 231 static void check(boolean cond) {if (cond) pass(); else fail();} 232 static void check(boolean cond, String m) {if (cond) pass(); else fail(m);} 233 static void equal(Object x, Object y) { 234 if (x == null ? y == null : x.equals(y)) pass(); 235 else fail(x + " not equal to " + y);} 236 public static void main(String[] args) throws Throwable { 237 try {realMain(args);} catch (Throwable t) {unexpected(t);} 238 System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed); 239 if (failed > 0) throw new AssertionError("Some tests failed");} 240 }