1 package com.atlassian.cache;
2
3 import java.lang.management.ManagementFactory;
4 import java.lang.management.ThreadInfo;
5 import java.lang.management.ThreadMXBean;
6 import java.util.concurrent.atomic.AtomicReference;
7
8 import javax.management.MXBean;
9
10 import com.atlassian.fugue.Throwables;
11
12
13
14
15
16
17 @SuppressWarnings ("ClassExplicitlyExtendsThread")
18 public abstract class TestThread extends Thread
19 {
20 private final AtomicReference<Throwable> caught = new AtomicReference<Throwable>();
21 private final AtomicReference<AssertionError> killed = new AtomicReference<AssertionError>();
22
23 public TestThread(final String name)
24 {
25 super("TestThread[" + name + ']');
26 setUncaughtExceptionHandler(BETTER_STACK_TRACER);
27 }
28
29 public void run()
30 {
31 try
32 {
33 go();
34 }
35 catch (Exception e)
36 {
37 caught.set(e);
38 throw Throwables.propagate(e, RuntimeException.class);
39 }
40 catch (Error err)
41 {
42 caught.set(err);
43 final AssertionError killer = killed.get();
44 if (killer != null)
45 {
46 throw killer;
47 }
48 throw err;
49 }
50 }
51
52 protected abstract void go() throws Exception;
53
54 private void verify()
55 {
56 final Throwable cause = caught.get();
57 if (cause != null)
58 {
59 final AssertionError err = new AssertionError("Failure in " + this);
60 err.initCause(cause);
61 throw err;
62 }
63 }
64
65 public static void runTest(TestThread... threads)
66 {
67 startAll(threads);
68 joinAll(threads);
69 verifyAll(threads);
70 }
71
72 private static void startAll(TestThread[] threads)
73 {
74 for (TestThread thd : threads)
75 {
76 thd.start();
77 }
78 }
79
80 private static void joinAll(TestThread[] threads)
81 {
82 final long deadline = System.currentTimeMillis() + 2 * Barrier.DEFAULT_TIMEOUT;
83 try
84 {
85 for (TestThread thd : threads)
86 {
87 final long timeleft = deadline - System.currentTimeMillis();
88 if (timeleft > 0L)
89 {
90 thd.join(timeleft);
91 }
92 else
93 {
94 thd.join(50L);
95 }
96 if (thd.isAlive())
97 {
98 kill(thd);
99 }
100 }
101 }
102 catch (InterruptedException ie)
103 {
104 throw new AssertionError(ie);
105 }
106 }
107
108 private static void kill(final TestThread thd)
109 {
110 final Error err = new AssertionError("Thread did not finish: " + thd);
111 ThreadInfo info = ManagementFactory.getThreadMXBean().getThreadInfo(thd.getId(), Integer.MAX_VALUE);
112 if (info != null)
113 {
114 final AssertionError killer = new AssertionError("Killed");
115 killer.setStackTrace(info.getStackTrace());
116 killer.initCause(err);
117 thd.killed.set(killer);
118 thd.stop();
119 }
120 throw err;
121 }
122
123 private static void verifyAll(TestThread[] threads)
124 {
125 for (TestThread thd : threads)
126 {
127 thd.verify();
128 }
129 }
130
131
132 static final Thread.UncaughtExceptionHandler BETTER_STACK_TRACER = new Thread.UncaughtExceptionHandler()
133 {
134 @Override
135 synchronized public void uncaughtException(final Thread t, final Throwable e)
136 {
137 System.err.println("=========================================");
138 System.err.println("Uncaught exception in thread: " + t);
139 e.printStackTrace(System.err);
140 System.err.println("-----------------------------------------");
141 System.err.println();
142 }
143 };
144 }