diff --git a/jdk/test/java/util/logging/LoggingDeadlock2.java b/jdk/test/java/util/logging/LoggingDeadlock2.java index 50de62abfcc..31bccfa5c3e 100644 --- a/jdk/test/java/util/logging/LoggingDeadlock2.java +++ b/jdk/test/java/util/logging/LoggingDeadlock2.java @@ -23,10 +23,9 @@ /* * @test - * @bug 6467152 - * @ignore until 6716076 is fixed + * @bug 6467152 6716076 6829503 * @summary deadlock occurs in LogManager initialization and JVM termination - * @author Serguei Spitsyn / Hittachi + * @author Serguei Spitsyn / Hitachi / Martin Buchholz * * @build LoggingDeadlock2 * @run main/timeout=15 LoggingDeadlock2 @@ -47,43 +46,195 @@ * This is a regression test for this bug. */ +import java.util.Arrays; +import java.util.List; +import java.util.Random; +import java.util.concurrent.CyclicBarrier; +import java.util.concurrent.atomic.AtomicInteger; import java.util.logging.LogManager; +import java.io.File; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; -public class LoggingDeadlock2 implements Runnable { - static final java.io.PrintStream out = System.out; - static Object lock = new Object(); - static int c = 0; - public static void main(String arg[]) { - out.println("\nThis test checks that there is no deadlock."); - out.println("If not crashed or timed-out then it is passed."); +public class LoggingDeadlock2 { + + public static void realMain(String arg[]) throws Throwable { try { - new Thread(new LoggingDeadlock2()).start(); - synchronized(lock) { - c++; - if (c == 2) lock.notify(); - else lock.wait(); - } + System.out.println(javaChildArgs); + ProcessBuilder pb = new ProcessBuilder(javaChildArgs); + ProcessResults r = run(pb.start()); + equal(r.exitValue(), 99); + equal(r.out(), ""); + equal(r.err(), ""); + } catch (Throwable t) { unexpected(t); } + } + + public static class JavaChild { + public static void main(String args[]) throws Throwable { + final CyclicBarrier startingGate = new CyclicBarrier(2); + final Throwable[] thrown = new Throwable[1]; + + // Some random variation, to help tickle races. + final Random rnd = new Random(); + final boolean dojoin = rnd.nextBoolean(); + final int JITTER = 1024; + final int iters1 = rnd.nextInt(JITTER); + final int iters2 = JITTER - iters1; + final AtomicInteger counter = new AtomicInteger(0); + + Thread exiter = new Thread() { + public void run() { + try { + startingGate.await(); + for (int i = 0; i < iters1; i++) + counter.getAndIncrement(); + System.exit(99); + } catch (Throwable t) { + t.printStackTrace(); + System.exit(86); + } + }}; + exiter.start(); + + startingGate.await(); + for (int i = 0; i < iters2; i++) + counter.getAndIncrement(); + // This may or may not result in a first call to + // Runtime.addShutdownHook after shutdown has already + // commenced. LogManager log = LogManager.getLogManager(); - out.println("Test passed"); - } - catch(Exception e) { - e.printStackTrace(); - out.println("Test FAILED"); // Not expected + + if (dojoin) { + exiter.join(); + if (thrown[0] != null) + throw new Error(thrown[0]); + check(counter.get() == JITTER); + } } } - public void run() { - try { - synchronized(lock) { - c++; - if (c == 2) lock.notify(); - else lock.wait(); - } - System.exit(1); + //---------------------------------------------------------------- + // The rest of this test is copied from ProcessBuilder/Basic.java + //---------------------------------------------------------------- + private static final String javaExe = + System.getProperty("java.home") + + File.separator + "bin" + File.separator + "java"; + + private static final String classpath = + System.getProperty("java.class.path"); + + private static final List javaChildArgs = + Arrays.asList(new String[] + { javaExe, "-classpath", classpath, + "LoggingDeadlock2$JavaChild"}); + + private static class ProcessResults { + private final String out; + private final String err; + private final int exitValue; + private final Throwable throwable; + + public ProcessResults(String out, + String err, + int exitValue, + Throwable throwable) { + this.out = out; + this.err = err; + this.exitValue = exitValue; + this.throwable = throwable; } - catch(Exception e) { - e.printStackTrace(); - out.println("Test FAILED"); // Not expected + + public String out() { return out; } + public String err() { return err; } + public int exitValue() { return exitValue; } + public Throwable throwable() { return throwable; } + + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("\n" + out() + "\n") + .append("\n" + err() + "\n") + .append("exitValue = " + exitValue + "\n"); + if (throwable != null) + sb.append(throwable.getStackTrace()); + return sb.toString(); } } + + private static class StreamAccumulator extends Thread { + private final InputStream is; + private final StringBuilder sb = new StringBuilder(); + private Throwable throwable = null; + + public String result () throws Throwable { + if (throwable != null) + throw throwable; + return sb.toString(); + } + + StreamAccumulator (InputStream is) { + this.is = is; + } + + public void run() { + try { + Reader r = new InputStreamReader(is); + char[] buf = new char[4096]; + int n; + while ((n = r.read(buf)) > 0) { + sb.append(buf,0,n); + } + } catch (Throwable t) { + throwable = t; + } finally { + try { is.close(); } + catch (Throwable t) { throwable = t; } + } + } + } + + private static ProcessResults run(Process p) { + Throwable throwable = null; + int exitValue = -1; + String out = ""; + String err = ""; + + StreamAccumulator outAccumulator = + new StreamAccumulator(p.getInputStream()); + StreamAccumulator errAccumulator = + new StreamAccumulator(p.getErrorStream()); + + try { + outAccumulator.start(); + errAccumulator.start(); + + exitValue = p.waitFor(); + + outAccumulator.join(); + errAccumulator.join(); + + out = outAccumulator.result(); + err = errAccumulator.result(); + } catch (Throwable t) { + throwable = t; + } + + return new ProcessResults(out, err, exitValue, throwable); + } + + //--------------------- Infrastructure --------------------------- + static volatile int passed = 0, failed = 0; + static void pass() {passed++;} + static void fail() {failed++; Thread.dumpStack();} + static void fail(String msg) {System.out.println(msg); fail();} + static void unexpected(Throwable t) {failed++; t.printStackTrace();} + static void check(boolean cond) {if (cond) pass(); else fail();} + static void check(boolean cond, String m) {if (cond) pass(); else fail(m);} + static void equal(Object x, Object y) { + if (x == null ? y == null : x.equals(y)) pass(); + else fail(x + " not equal to " + y);} + public static void main(String[] args) throws Throwable { + try {realMain(args);} catch (Throwable t) {unexpected(t);} + System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed); + if (failed > 0) throw new AssertionError("Some tests failed");} }