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 */ 40 41 /** 42 * This test is checking for a deadlock between 43 * LogManager$LoggerContext.findLogger() and 44 * LogManager.drainLoggerRefQueueBounded() (which could happen by calling 45 * Logger.getLogger() and LogManager.readConfiguration() in different threads) 46 */ 47 public class DrainFindDeadlockTest { 48 private LogManager mgr = LogManager.getLogManager(); 49 private final static int MAX_ITERATIONS = 100; 50 51 // Get a ThreadMXBean so we can check for deadlock. N.B. this may 52 // not be supported on all platforms, which means we will have to 53 // resort to the traditional test timeout method. However, if 54 // we have the support we'll get the deadlock details if one 55 // is detected. 56 private final static ThreadMXBean threadMXBean = 57 ManagementFactory.getThreadMXBean(); 58 private final boolean threadMXBeanDeadlockSupported = 59 threadMXBean.isSynchronizerUsageSupported(); 60 61 private volatile boolean terminate = false; 62 private volatile Exception failed = null; 63 64 public static void main(String... args) throws IOException, Exception { 65 new DrainFindDeadlockTest().testForDeadlock(); 66 } 67 68 public static void randomDelay() { 69 int runs = (int) Math.random() * 1000000; 70 int c = 0; 71 72 for (int i=0; i<runs; ++i) { 73 c=c+i; 74 } 75 } 76 77 public void fail(Exception failure) { 78 if (failed == null) { 79 failed = failure; 80 } else { 81 failure.printStackTrace(); 82 } 83 terminate = true; 84 } 85 86 public void testForDeadlock() throws IOException, Exception { 87 System.out.println("Deadlock detection " 88 + (threadMXBeanDeadlockSupported ? "is" : "is not") + 89 " available."); 90 Thread setup = new Thread(new SetupLogger(), "SetupLogger"); 91 Thread readConfig = new Thread(new ReadConfig(), "ReadConfig"); 92 Thread check = new Thread(new DeadlockChecker(setup, readConfig), 93 "DeadlockChecker"); 94 95 // make the threads daemon threads so they will go away when the 96 // test exits 97 setup.setDaemon(true); 98 readConfig.setDaemon(true); 99 check.setDaemon(true); 100 101 check.start(); setup.start(); readConfig.start(); 102 try { 103 check.join(); 104 } catch (InterruptedException ex) { 105 ex.printStackTrace(); 106 } 107 108 if (failed == null) { 109 terminate = true; 110 try { 111 readConfig.join(); 112 setup.join(); 113 } catch (InterruptedException ex) { 114 ex.printStackTrace(); 115 } 116 System.out.println("Test passed"); 117 } else { 118 System.err.println("Test Failed: " + failed); 119 throw failed; 120 } 121 } 122 123 class SetupLogger implements Runnable { 124 Logger logger = null; 125 126 @Override 127 public void run() { 128 System.out.println("Running " + Thread.currentThread().getName()); 129 int i = 0; 130 while (!terminate) { 131 logger = Logger.getLogger("DrainFindDeadlockTest"+(i++)); 132 DrainFindDeadlockTest.randomDelay(); 133 } 134 } 135 } 136 137 class ReadConfig implements Runnable { 138 @Override 139 public void run() { 140 System.out.println("Running " + Thread.currentThread().getName()); 141 while (!terminate) { 142 try { 143 mgr.readConfiguration(); 144 } catch (IOException | SecurityException ex) { 145 fail(new RuntimeException("FAILED: test setup problem", ex)); 146 } 147 DrainFindDeadlockTest.randomDelay(); 148 } 149 } 150 } 151 152 class DeadlockChecker implements Runnable { 153 final Thread x, y; 154 final long[] threadIds; 155 DeadlockChecker(Thread t1, Thread t2) { 156 this.x = t1; 157 this.y = t2; 158 threadIds = new long[] { t1.getId(), t2.getId() }; 159 } 160 161 boolean threadsBlocked(ThreadInfo[] xyThreadInfos) { 162 for (ThreadInfo threadInfo : xyThreadInfos) { 163 switch (threadInfo.getThreadState()) { 164 case BLOCKED: 165 case WAITING: 166 case TIMED_WAITING: 167 continue; 168 default: 169 System.out.println("Thread " + threadInfo.getThreadName() + 170 " is not blocked: " + threadInfo.getThreadState()); 171 return false; 172 } 173 } 174 return xyThreadInfos.length > 0; 175 } 176 177 long[] findDeadlockedThreads() { 178 // It seems that calling ThreadMXBean.findDeadlockedThreads() 179 // is not always reliable. Until that is fixed we will only 180 // call it if we find that our two threads are simultaneously 181 // BLOCKED or WAITING. 182 System.out.println("checking thread infos"); 183 ThreadInfo[] xyThreadInfos = threadMXBean.getThreadInfo(threadIds, true, true); 184 if (!threadsBlocked(xyThreadInfos)) return null; 185 /* 186 if (xyThreadInfo != null && xyThreadInfos.length == 2) { 187 if (xyThreadInfo[0].getLockOwnerId() != xyThreadInfo[1].getThreadId()) { 188 return null; 189 } 190 if (xyThreadInfo[1].getLockOwnerId() != xyThreadInfo[0].getThreadId()) { 191 return null; 192 } 193 } 194 */ 195 System.out.println("checking for deadlock"); 196 long[] deadlockedThreads = threadMXBean.findDeadlockedThreads(); 197 if (deadlockedThreads != null) { 198 xyThreadInfos = threadMXBean.getThreadInfo(deadlockedThreads, true, true); 199 if (!threadsBlocked(xyThreadInfos)) return null; 200 } 201 return deadlockedThreads; 202 } 203 204 void checkState() { 205 // System.out.println("checkstate"); 206 boolean isXblocked = x.getState().equals(State.BLOCKED); 207 boolean isYblocked = y.getState().equals(State.BLOCKED); 208 long[] deadlockedThreads = null; 209 210 if (isXblocked && isYblocked) { 211 System.out.println("threads blocked"); 212 // they are both blocked, but this doesn't necessarily mean 213 // they are deadlocked 214 if (threadMXBeanDeadlockSupported) { 215 deadlockedThreads = findDeadlockedThreads(); 216 } else { 217 System.out.println("Can't check for deadlock"); 218 } 219 if (deadlockedThreads != null) { 220 System.out.println("We detected a deadlock! "); 221 ThreadInfo[] threadInfos = threadMXBean.getThreadInfo( 222 deadlockedThreads, true, true); 223 for (ThreadInfo threadInfo: threadInfos) { 224 System.out.println("Thread " + threadInfo.getThreadName() + " blocked on " + 225 threadInfo.getLockName() + " owned by " + threadInfo.getLockOwnerName()); 226 } 227 for (ThreadInfo threadInfo: threadInfos) { 228 System.out.println(threadInfo); 229 } 230 fail(new RuntimeException("TEST FAILED: Deadlock detected")); 231 } else if (!threadMXBeanDeadlockSupported) { 232 System.out.println("We may have a deadlock"); 233 Map<Thread, StackTraceElement[]> threadMap = 234 Thread.getAllStackTraces(); 235 dumpStack(threadMap.get(x), x); 236 dumpStack(threadMap.get(y), y); 237 } 238 } 239 } 240 241 private void dumpStack(StackTraceElement[] aStackElt, Thread aThread) { 242 if (aStackElt != null) { 243 System.out.println("Thread:" + aThread.getName() + ": " + 244 aThread.getState()); 245 for (StackTraceElement element: aStackElt) { 246 System.out.println(" " + element); 247 } 248 } 249 } 250 251 @Override 252 public void run() { 253 System.out.println("Running " + Thread.currentThread().getName()); 254 for (int i=0; i < MAX_ITERATIONS*2; i++) { 255 if (terminate) return; 256 checkState(); 257 try { 258 Thread.sleep(10); 259 } catch (InterruptedException ex) { 260 }; 261 } 262 } 263 } 264 }