1 /*
   2  * Copyright (c) 2010, 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 import java.lang.management.ThreadInfo;
  24 import java.lang.management.ThreadMXBean;
  25 import java.lang.Thread.State;
  26 import java.io.IOException;
  27 import java.lang.management.ManagementFactory;
  28 import java.util.logging.LogManager;
  29 import java.util.logging.Logger;
  30 import java.util.Map;
  31 
  32 /**
  33  * @test
  34  * @bug 8010939
  35  * @author jim.gish@oracle.com
  36  * @run main/timeout=15 Bug8010939
  37  */
  38 public class Bug8010939 {
  39     private LogManager mgr = LogManager.getLogManager();
  40     private final static int MAX_ITERATIONS=1000;
  41     private ThreadMXBean threadMXBean = null;
  42     private boolean threadMXBeanDeadlockSupported = true;  // assume true for now
  43     private static volatile boolean deadlockDetected = false;
  44 
  45     public static void main(String... args) throws IOException, Exception {
  46         new Bug8010939().testForDeadlock();
  47         if (deadlockDetected) {
  48             System.out.println( "Deadlock detected.");
  49         }
  50     }
  51 
  52     public static void randomDelay() {
  53         int runs = (int) Math.random() * 1000000;
  54         int c = 0;
  55 
  56         for (int i = 0; i < runs; ++i) {
  57             c = c + i;
  58         }
  59     }
  60 
  61     public void testForDeadlock() throws IOException, Exception {
  62         // Get a ThreadMXBean so we can check for deadlock.  N.B. this may
  63         // not be supported on all platforms, which means we will have to
  64         // resort to the traditional test timeout method. However, if
  65         // we have the support we'll get the deadlock details if one
  66         // is detected.
  67         threadMXBean = ManagementFactory.getThreadMXBean();
  68 
  69         Thread setup = new Thread( new SetupLogger(), "SetupLogger" );
  70         Thread readConfig = new Thread( new ReadConfig(), "ReadConfig" );
  71         Thread check = new Thread( new DeadlockChecker(setup, readConfig),
  72                                    "DeadlockChecker" );
  73 
  74         setup.start(); readConfig.start(); check.start();
  75         try {
  76             check.join();
  77         } catch (InterruptedException ex) {
  78             ex.printStackTrace();
  79         }
  80         if (deadlockDetected) {
  81             throw new Exception( "TEST FAILED: Deadlock detected" );
  82         }
  83         try {
  84             readConfig.join();
  85             setup.join();
  86         } catch (InterruptedException ex) {
  87             ex.printStackTrace();
  88         }
  89         System.out.println("Test passed");
  90     }
  91 
  92     class SetupLogger implements Runnable {
  93         Logger logger = null;
  94 
  95         @Override
  96         public void run() {
  97             for (int i=0;i<MAX_ITERATIONS; i++) {
  98                 logger = Logger.getLogger("Bug8010939"+i);
  99                 mgr.addLogger(logger);
 100                 Bug8010939.randomDelay();
 101             }
 102         }
 103     }
 104 
 105     class ReadConfig implements Runnable {
 106         @Override
 107         public void run() {
 108             for (int i=0; i<MAX_ITERATIONS; i++) {
 109                 try {
 110                     mgr.readConfiguration();
 111                 } catch (Exception ex) {
 112                     ex.printStackTrace();
 113                 }
 114                 Bug8010939.randomDelay();
 115             }
 116         }
 117     }
 118 
 119     class DeadlockChecker implements Runnable {
 120         Thread t1, t2;
 121 
 122         DeadlockChecker(Thread t1, Thread t2) {
 123             this.t1 = t1;
 124             this.t2 = t2;
 125         }
 126 
 127         boolean checkState( Thread x, Thread y ) {
 128             System.out.println( "In checkState" );
 129             boolean isXblocked = x.getState().equals(State.BLOCKED);
 130             boolean isYblocked = y.getState().equals(State.BLOCKED);
 131             long[] deadlockedThreads = null;
 132             boolean isDeadlocked = false; // until we find out otherwise
 133 
 134             if (isXblocked && isYblocked ) {
 135                 // they are both blocked, but this doesn't necessarily mean
 136                 // they are deadlocked
 137                 if (threadMXBeanDeadlockSupported) {
 138                     try {
 139                         deadlockedThreads = threadMXBean.findDeadlockedThreads();
 140                     } catch (UnsupportedOperationException ex) {
 141                         System.out.println( "INFO: This platform does not support " +
 142                                 "ThreadMXBean.findDeadlockedThreads() " +
 143                                 "so, we'll continue waiting for test timeout.");
 144                         threadMXBeanDeadlockSupported = false;
 145                     }
 146                 }
 147                 if (deadlockedThreads != null) {
 148                     System.out.println( "We detected a deadlock! ");
 149                     ThreadInfo[] threadInfos = threadMXBean.getThreadInfo(
 150                             deadlockedThreads, true, true);
 151                     for (ThreadInfo threadInfo: threadInfos) {
 152                         System.out.println( threadInfo );
 153                     }
 154                     return true; // we know we're deadlocked
 155                 }
 156                 System.out.println( "We may have a deadlock");
 157                 Map<Thread, StackTraceElement[]> threadMap =
 158                         Thread.getAllStackTraces();
 159                 dumpStack( threadMap.get(x), x);
 160                 dumpStack( threadMap.get(y), y);
 161             }
 162             return isDeadlocked;
 163         }
 164 
 165         private void dumpStack(StackTraceElement[] aStackElt, Thread aThread) {
 166             if (aStackElt != null) {
 167                  System.out.println( "Thread:" + aThread.getName() + ": " +
 168                                     aThread.getState());
 169                  for (StackTraceElement element: aStackElt) {
 170                     System.out.println( "   " + element );
 171                  }
 172             }
 173         }
 174 
 175         @Override
 176         public void run() {
 177             boolean deadlockFound = false;
 178             for (int i=0; i<MAX_ITERATIONS && !deadlockFound ; i++) {
 179                 deadlockFound = checkState(t1, t2);
 180                 Bug8010939.randomDelay();
 181             }
 182             if (deadlockFound) {
 183                 deadlockDetected = true;
 184             }
 185         }
 186     }
 187 }