1 /*
   2  * Copyright (c) 2013, 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  * @summary check for deadlock between findLogger() and drainLoggerRefQueueBounded()
  36  * @author jim.gish@oracle.com
  37  * @build DrainFindDeadlockTest
  38  * @run main/othervm/timeout=10 DrainFindDeadlockTest
  39  * @key randomness
  40  */
  41 
  42 /**
  43  * This test is checking for a deadlock between
  44  * LogManager$LoggerContext.findLogger() and
  45  * LogManager.drainLoggerRefQueueBounded() (which could happen by calling
  46  * Logger.getLogger() and LogManager.readConfiguration() in different threads)
  47  */
  48 public class DrainFindDeadlockTest {
  49     private LogManager mgr = LogManager.getLogManager();
  50     private final static int MAX_ITERATIONS = 100;
  51 
  52     // Get a ThreadMXBean so we can check for deadlock.  N.B. this may
  53     // not be supported on all platforms, which means we will have to
  54     // resort to the traditional test timeout method. However, if
  55     // we have the support we'll get the deadlock details if one
  56     // is detected.
  57     private final static ThreadMXBean threadMXBean =
  58             ManagementFactory.getThreadMXBean();
  59     private final boolean threadMXBeanDeadlockSupported =
  60             threadMXBean.isSynchronizerUsageSupported();
  61 
  62     public static void main(String... args) throws IOException, Exception {
  63         new DrainFindDeadlockTest().testForDeadlock();
  64     }
  65 
  66     public static void randomDelay() {
  67         int runs = (int) Math.random() * 1000000;
  68         int c = 0;
  69 
  70         for (int i=0; i<runs; ++i) {
  71             c=c+i;
  72         }
  73     }
  74 
  75     public void testForDeadlock() throws IOException, Exception {
  76         System.out.println("Deadlock detection "
  77                 + (threadMXBeanDeadlockSupported ? "is" : "is not") +
  78                             " available.");
  79         Thread setup = new Thread(new SetupLogger(), "SetupLogger");
  80         Thread readConfig = new Thread(new ReadConfig(), "ReadConfig");
  81         Thread check = new Thread(new DeadlockChecker(setup, readConfig),
  82                                    "DeadlockChecker");
  83 
  84         // make the threads daemon threads so they will go away when the
  85         // test exits
  86         setup.setDaemon(true);
  87         readConfig.setDaemon(true);
  88         check.setDaemon(true);
  89 
  90         check.start(); setup.start(); readConfig.start();
  91         try {
  92             check.join();
  93         } catch (InterruptedException ex) {
  94             ex.printStackTrace();
  95         }
  96         try {
  97             readConfig.join();
  98             setup.join();
  99         } catch (InterruptedException ex) {
 100             ex.printStackTrace();
 101         }
 102         System.out.println("Test passed");
 103     }
 104 
 105     class SetupLogger implements Runnable {
 106         Logger logger = null;
 107 
 108         @Override
 109         public void run() {
 110             System.out.println("Running " + Thread.currentThread().getName());
 111 
 112             for (int i=0; i < MAX_ITERATIONS; i++) {
 113                 logger = Logger.getLogger("DrainFindDeadlockTest"+i);
 114                 DrainFindDeadlockTest.randomDelay();
 115             }
 116         }
 117     }
 118 
 119     class ReadConfig implements Runnable {
 120         @Override
 121         public void run() {
 122             System.out.println("Running " + Thread.currentThread().getName());
 123             for (int i=0; i < MAX_ITERATIONS; i++) {
 124                 try {
 125                     mgr.readConfiguration();
 126                 } catch (IOException | SecurityException ex) {
 127                     throw new RuntimeException("FAILED: test setup problem", ex);
 128                 }
 129                 DrainFindDeadlockTest.randomDelay();
 130             }
 131         }
 132     }
 133 
 134     class DeadlockChecker implements Runnable {
 135         Thread t1, t2;
 136 
 137         DeadlockChecker(Thread t1, Thread t2) {
 138             this.t1 = t1;
 139             this.t2 = t2;
 140         }
 141 
 142         void checkState(Thread x, Thread y) {
 143             //            System.out.println("checkstate");
 144             boolean isXblocked = x.getState().equals(State.BLOCKED);
 145             boolean isYblocked = y.getState().equals(State.BLOCKED);
 146             long[] deadlockedThreads = null;
 147 
 148             if (isXblocked && isYblocked) {
 149                 System.out.println("threads blocked");
 150                 // they are both blocked, but this doesn't necessarily mean
 151                 // they are deadlocked
 152                 if (threadMXBeanDeadlockSupported) {
 153                     System.out.println("checking for deadlock");
 154                     deadlockedThreads = threadMXBean.findDeadlockedThreads();
 155                 } else {
 156                     System.out.println("Can't check for deadlock");
 157                 }
 158                 if (deadlockedThreads != null) {
 159                     System.out.println("We detected a deadlock! ");
 160                     ThreadInfo[] threadInfos = threadMXBean.getThreadInfo(
 161                             deadlockedThreads, true, true);
 162                     for (ThreadInfo threadInfo: threadInfos) {
 163                         System.out.println(threadInfo);
 164                     }
 165                     throw new RuntimeException("TEST FAILED: Deadlock detected");
 166                 }
 167                 System.out.println("We may have a deadlock");
 168                 Map<Thread, StackTraceElement[]> threadMap =
 169                         Thread.getAllStackTraces();
 170                 dumpStack(threadMap.get(x), x);
 171                 dumpStack(threadMap.get(y), y);
 172             }
 173         }
 174 
 175         private void dumpStack(StackTraceElement[] aStackElt, Thread aThread) {
 176             if (aStackElt != null) {
 177                  System.out.println("Thread:" + aThread.getName() + ": " +
 178                                     aThread.getState());
 179                  for (StackTraceElement element: aStackElt) {
 180                     System.out.println("   " + element);
 181                  }
 182             }
 183         }
 184 
 185         @Override
 186         public void run() {
 187             System.out.println("Running " + Thread.currentThread().getName());
 188             for (int i=0; i < MAX_ITERATIONS*2; i++) {
 189                 checkState(t1, t2);
 190                 try {
 191                     Thread.sleep(10);
 192                 } catch (InterruptedException ex) {
 193                 };
 194             }
 195         }
 196     }
 197 }