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