blob: af1eed48f04c9c264a254bb33283e422001cac15 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2007 Sun Microsystems, Inc. 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
20 * CA 95054 USA or visit www.sun.com if you need additional information or
21 * have any questions.
22 */
23
24/*
25 * @test
26 * @bug 6450200 6450205 6450207 6450211
27 * @summary Test proper handling of tasks that terminate abruptly
28 * @run main/othervm -XX:-UseVMInterruptibleIO ThrowingTasks
29 * @author Martin Buchholz
30 */
31
32import java.security.*;
33import java.util.*;
34import java.util.concurrent.*;
35import java.util.concurrent.atomic.*;
36
37public class ThrowingTasks {
38 final static Random rnd = new Random();
39
40 @SuppressWarnings("serial")
41 static class UncaughtExceptions
42 extends ConcurrentHashMap<Class<?>, Integer> {
43
44 void inc(Class<?> key) {
45 for (;;) {
46 Integer i = get(key);
47 if (i == null) {
48 if (putIfAbsent(key, 1) == null)
49 return;
50 } else {
51 if (replace(key, i, i + 1))
52 return;
53 }
54 }
55 }
56 }
57
58 @SuppressWarnings("serial")
59 static class UncaughtExceptionsTable
60 extends Hashtable<Class<?>, Integer> {
61
62 synchronized void inc(Class<?> key) {
63 Integer i = get(key);
64 put(key, (i == null) ? 1 : i + 1);
65 }
66 }
67
68 final static UncaughtExceptions uncaughtExceptions
69 = new UncaughtExceptions();
70 final static UncaughtExceptionsTable uncaughtExceptionsTable
71 = new UncaughtExceptionsTable();
72 final static AtomicLong totalUncaughtExceptions
73 = new AtomicLong(0);
74 final static CountDownLatch uncaughtExceptionsLatch
75 = new CountDownLatch(24);
76
77 final static Thread.UncaughtExceptionHandler handler
78 = new Thread.UncaughtExceptionHandler() {
79 public void uncaughtException(Thread t, Throwable e) {
80 check(! Thread.currentThread().isInterrupted());
81 totalUncaughtExceptions.getAndIncrement();
82 uncaughtExceptions.inc(e.getClass());
83 uncaughtExceptionsTable.inc(e.getClass());
84 uncaughtExceptionsLatch.countDown();
85 }};
86
87 final static ThreadGroup tg = new ThreadGroup("Flaky");
88
89 final static ThreadFactory tf = new ThreadFactory() {
90 public Thread newThread(Runnable r) {
91 Thread t = new Thread(tg, r);
92 t.setUncaughtExceptionHandler(handler);
93 return t;
94 }};
95
96 final static RuntimeException rte = new RuntimeException();
97 final static Error error = new Error();
98 final static Throwable weird = new Throwable();
99 final static Exception checkedException = new Exception();
100
101 static class Thrower implements Runnable {
102 Throwable t;
103 Thrower(Throwable t) { this.t = t; }
104 @SuppressWarnings("deprecation")
105 public void run() { if (t != null) Thread.currentThread().stop(t); }
106 }
107
108 final static Thrower noThrower = new Thrower(null);
109 final static Thrower rteThrower = new Thrower(rte);
110 final static Thrower errorThrower = new Thrower(error);
111 final static Thrower weirdThrower = new Thrower(weird);
112 final static Thrower checkedThrower = new Thrower(checkedException);
113
114 final static List<Thrower> throwers = Arrays.asList(
115 noThrower, rteThrower, errorThrower, weirdThrower, checkedThrower);
116
117 static class Flaky implements Runnable {
118 final Runnable beforeExecute;
119 final Runnable execute;
120 Flaky(Runnable beforeExecute,
121 Runnable execute) {
122 this.beforeExecute = beforeExecute;
123 this.execute = execute;
124 }
125 public void run() { execute.run(); }
126 }
127
128 static final List<Flaky> flakes = new ArrayList<Flaky>();
129 static {
130 for (Thrower x : throwers)
131 for (Thrower y : throwers)
132 flakes.add(new Flaky(x, y));
133 Collections.shuffle(flakes);
134 }
135
136 static final CountDownLatch allStarted = new CountDownLatch(flakes.size());
137 static final CountDownLatch allContinue = new CountDownLatch(1);
138
139 static class PermissiveSecurityManger extends SecurityManager {
140 public void checkPermission(Permission p) { /* bien sur, Monsieur */ }
141 }
142
143 static void checkTerminated(ThreadPoolExecutor tpe) {
144 try {
145 check(tpe.getQueue().isEmpty());
146 check(tpe.isShutdown());
147 check(tpe.isTerminated());
148 check(! tpe.isTerminating());
149 equal(tpe.getActiveCount(), 0);
150 equal(tpe.getPoolSize(), 0);
151 equal(tpe.getTaskCount(), tpe.getCompletedTaskCount());
152 check(tpe.awaitTermination(0, TimeUnit.SECONDS));
153 } catch (Throwable t) { unexpected(t); }
154 }
155
156 static class CheckingExecutor extends ThreadPoolExecutor {
157 CheckingExecutor() {
158 super(10, 10,
159 1L, TimeUnit.HOURS,
160 new LinkedBlockingQueue<Runnable>(),
161 tf);
162 }
163 @Override protected void beforeExecute(Thread t, Runnable r) {
164 allStarted.countDown();
165 if (allStarted.getCount() < getCorePoolSize())
166 try { allContinue.await(); }
167 catch (InterruptedException x) { unexpected(x); }
168 beforeExecuteCount.getAndIncrement();
169 check(! isTerminated());
170 ((Flaky)r).beforeExecute.run();
171 }
172 @Override protected void afterExecute(Runnable r, Throwable t) {
173 //System.out.println(tg.activeCount());
174 afterExecuteCount.getAndIncrement();
175 check(((Thrower)((Flaky)r).execute).t == t);
176 check(! isTerminated());
177 }
178 @Override protected void terminated() {
179 try {
180 terminatedCount.getAndIncrement();
181 if (rnd.nextBoolean()) {
182 check(isShutdown());
183 check(isTerminating());
184 check(! isTerminated());
185 check(! awaitTermination(0L, TimeUnit.MINUTES));
186 }
187 } catch (Throwable t) { unexpected(t); }
188 }
189 }
190
191 static final AtomicInteger beforeExecuteCount = new AtomicInteger(0);
192 static final AtomicInteger afterExecuteCount = new AtomicInteger(0);
193 static final AtomicInteger terminatedCount = new AtomicInteger(0);
194
195 private static void realMain(String[] args) throws Throwable {
196 if (rnd.nextBoolean())
197 System.setSecurityManager(new PermissiveSecurityManger());
198
199 CheckingExecutor tpe = new CheckingExecutor();
200
201 for (Runnable task : flakes)
202 tpe.execute(task);
203
204 if (rnd.nextBoolean()) {
205 allStarted.await();
206 equal(tpe.getTaskCount(),
207 (long) flakes.size());
208 equal(tpe.getCompletedTaskCount(),
209 (long) flakes.size() - tpe.getCorePoolSize());
210 }
211 allContinue.countDown();
212
213 //System.out.printf("thread count = %d%n", tg.activeCount());
214 uncaughtExceptionsLatch.await();
215
216 while (tg.activeCount() != tpe.getCorePoolSize() ||
217 tg.activeCount() != tpe.getCorePoolSize())
218 Thread.sleep(10);
219 equal(tg.activeCount(), tpe.getCorePoolSize());
220
221 tpe.shutdown();
222
223 check(tpe.awaitTermination(10L, TimeUnit.MINUTES));
224 checkTerminated(tpe);
225
226 //while (tg.activeCount() > 0) Thread.sleep(10);
227 //System.out.println(uncaughtExceptions);
228 List<Map<Class<?>, Integer>> maps
229 = new ArrayList<Map<Class<?>, Integer>>();
230 maps.add(uncaughtExceptions);
231 maps.add(uncaughtExceptionsTable);
232 for (Map<Class<?>, Integer> map : maps) {
233 equal(map.get(Exception.class), throwers.size());
234 equal(map.get(weird.getClass()), throwers.size());
235 equal(map.get(Error.class), throwers.size() + 1 + 2);
236 equal(map.get(RuntimeException.class), throwers.size() + 1);
237 equal(map.size(), 4);
238 }
239 equal(totalUncaughtExceptions.get(), 4L*throwers.size() + 4L);
240
241 equal(beforeExecuteCount.get(), flakes.size());
242 equal(afterExecuteCount.get(), throwers.size());
243 equal(tpe.getCompletedTaskCount(), (long) flakes.size());
244 equal(terminatedCount.get(), 1);
245
246 // check for termination operation idempotence
247 tpe.shutdown();
248 tpe.shutdownNow();
249 check(tpe.awaitTermination(10L, TimeUnit.MINUTES));
250 checkTerminated(tpe);
251 equal(terminatedCount.get(), 1);
252 }
253
254 //--------------------- Infrastructure ---------------------------
255 static volatile int passed = 0, failed = 0;
256 static void pass() {passed++;}
257 static void fail() {failed++; Thread.dumpStack();}
258 static void fail(String msg) {System.out.println(msg); fail();}
259 static void unexpected(Throwable t) {failed++; t.printStackTrace();}
260 static void check(boolean cond) {if (cond) pass(); else fail();}
261 static void equal(Object x, Object y) {
262 if (x == null ? y == null : x.equals(y)) pass();
263 else fail(x + " not equal to " + y);}
264 public static void main(String[] args) throws Throwable {
265 try {realMain(args);} catch (Throwable t) {unexpected(t);}
266 System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
267 if (failed > 0) throw new AssertionError("Some tests failed");}
268}