jdk/test/java/util/logging/LoggingDeadlock2.java
changeset 2631 29398881792c
parent 2401 96065ac71318
child 2638 d4447065286a
--- a/jdk/test/java/util/logging/LoggingDeadlock2.java	Mon Apr 20 21:23:47 2009 -0700
+++ b/jdk/test/java/util/logging/LoggingDeadlock2.java	Mon Apr 20 21:53:38 2009 -0700
@@ -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();
+
+            if (dojoin) {
+                exiter.join();
+                if (thrown[0] != null)
+                    throw new Error(thrown[0]);
+                check(counter.get() == JITTER);
             }
-            LogManager log = LogManager.getLogManager();
-            out.println("Test passed");
-        }
-        catch(Exception e) {
-            e.printStackTrace();
-            out.println("Test FAILED"); // Not expected
         }
     }
 
-    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<String> 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("<STDOUT>\n" + out() + "</STDOUT>\n")
+                .append("<STDERR>\n" + err() + "</STDERR>\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");}
 }