1 /**
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements.  See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership.  The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the
7  * "License"); you may not use this file except in compliance
8  * with the License.  You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18 package org.apache.hadoop.test;
19 
20 import java.io.PrintWriter;
21 import java.io.StringWriter;
22 import java.util.concurrent.BrokenBarrierException;
23 import java.util.concurrent.CyclicBarrier;
24 import java.util.concurrent.locks.Lock;
25 import java.util.concurrent.locks.ReentrantLock;
26 
27 import org.junit.Assert;
28 import org.junit.Test;
29 import org.junit.runner.notification.Failure;
30 
31 public class TestTimedOutTestsListener {
32 
33   public static class Deadlock {
34     private CyclicBarrier barrier = new CyclicBarrier(6);
35 
Deadlock()36     public Deadlock() {
37       DeadlockThread[] dThreads = new DeadlockThread[6];
38 
39       Monitor a = new Monitor("a");
40       Monitor b = new Monitor("b");
41       Monitor c = new Monitor("c");
42       dThreads[0] = new DeadlockThread("MThread-1", a, b);
43       dThreads[1] = new DeadlockThread("MThread-2", b, c);
44       dThreads[2] = new DeadlockThread("MThread-3", c, a);
45 
46       Lock d = new ReentrantLock();
47       Lock e = new ReentrantLock();
48       Lock f = new ReentrantLock();
49 
50       dThreads[3] = new DeadlockThread("SThread-4", d, e);
51       dThreads[4] = new DeadlockThread("SThread-5", e, f);
52       dThreads[5] = new DeadlockThread("SThread-6", f, d);
53 
54       // make them daemon threads so that the test will exit
55       for (int i = 0; i < 6; i++) {
56         dThreads[i].setDaemon(true);
57         dThreads[i].start();
58       }
59     }
60 
61     class DeadlockThread extends Thread {
62       private Lock lock1 = null;
63 
64       private Lock lock2 = null;
65 
66       private Monitor mon1 = null;
67 
68       private Monitor mon2 = null;
69 
70       private boolean useSync;
71 
DeadlockThread(String name, Lock lock1, Lock lock2)72       DeadlockThread(String name, Lock lock1, Lock lock2) {
73         super(name);
74         this.lock1 = lock1;
75         this.lock2 = lock2;
76         this.useSync = true;
77       }
78 
DeadlockThread(String name, Monitor mon1, Monitor mon2)79       DeadlockThread(String name, Monitor mon1, Monitor mon2) {
80         super(name);
81         this.mon1 = mon1;
82         this.mon2 = mon2;
83         this.useSync = false;
84       }
85 
run()86       public void run() {
87         if (useSync) {
88           syncLock();
89         } else {
90           monitorLock();
91         }
92       }
93 
syncLock()94       private void syncLock() {
95         lock1.lock();
96         try {
97           try {
98             barrier.await();
99           } catch (Exception e) {
100           }
101           goSyncDeadlock();
102         } finally {
103           lock1.unlock();
104         }
105       }
106 
goSyncDeadlock()107       private void goSyncDeadlock() {
108         try {
109           barrier.await();
110         } catch (Exception e) {
111         }
112         lock2.lock();
113         throw new RuntimeException("should not reach here.");
114       }
115 
monitorLock()116       private void monitorLock() {
117         synchronized (mon1) {
118           try {
119             barrier.await();
120           } catch (Exception e) {
121           }
122           goMonitorDeadlock();
123         }
124       }
125 
goMonitorDeadlock()126       private void goMonitorDeadlock() {
127         try {
128           barrier.await();
129         } catch (Exception e) {
130         }
131         synchronized (mon2) {
132           throw new RuntimeException(getName() + " should not reach here.");
133         }
134       }
135     }
136 
137     class Monitor {
138       String name;
139 
Monitor(String name)140       Monitor(String name) {
141         this.name = name;
142       }
143     }
144 
145   }
146 
147   @Test(timeout=500)
testThreadDumpAndDeadlocks()148   public void testThreadDumpAndDeadlocks() throws Exception {
149     new Deadlock();
150     String s = null;
151     while (true) {
152       s = TimedOutTestsListener.buildDeadlockInfo();
153       if (s != null)
154         break;
155       Thread.sleep(100);
156     }
157 
158     Assert.assertEquals(3, countStringOccurrences(s, "BLOCKED"));
159 
160     Failure failure = new Failure(
161         null, new Exception(TimedOutTestsListener.TEST_TIMED_OUT_PREFIX));
162     StringWriter writer = new StringWriter();
163     new TimedOutTestsListener(new PrintWriter(writer)).testFailure(failure);
164     String out = writer.toString();
165 
166     Assert.assertTrue(out.contains("THREAD DUMP"));
167     Assert.assertTrue(out.contains("DEADLOCKS DETECTED"));
168 
169     System.out.println(out);
170   }
171 
countStringOccurrences(String s, String substr)172   private int countStringOccurrences(String s, String substr) {
173     int n = 0;
174     int index = 0;
175     while ((index = s.indexOf(substr, index) + 1) != 0) {
176       n++;
177     }
178     return n;
179   }
180 
181 }
182