/**
* Copyright (C) 2009-2013 FoundationDB, LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.foundationdb.server.test.mt.util;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;
import static org.junit.Assert.assertEquals;
public class ThreadHelper
{
private static final long DEFAULT_TIMEOUT_MILLIS = 5 * 1000;
public static class UncaughtHandler implements Thread.UncaughtExceptionHandler {
public final Map<Thread, Throwable> thrown = Collections.synchronizedMap(new HashMap<Thread, Throwable>());
@Override
public void uncaughtException(Thread t, Throwable e) {
thrown.put(t, e);
}
@Override
public String toString() {
Map<String,String> nameToError = new TreeMap<>();
for(Entry<Thread, Throwable> entry : thrown.entrySet()) {
nameToError.put(entry.getKey().getName(), entry.getValue().getMessage());
}
return nameToError.toString();
}
}
public static UncaughtHandler start(Thread... threads) {
return start(Arrays.asList(threads));
}
public static void join(UncaughtHandler handler, long timeoutMillis, Thread... threads) {
join(handler, timeoutMillis, Arrays.asList(threads));
}
public static UncaughtHandler startAndJoin(long timeoutMillis, Thread... threads) {
return startAndJoin(timeoutMillis, Arrays.asList(threads));
}
public static void runAndCheck(Thread... threads) {
runAndCheck(Arrays.asList(threads));
}
public static void runAndCheck(long timeoutMillis, Thread... threads) {
runAndCheck(timeoutMillis, Arrays.asList(threads));
}
public static UncaughtHandler start(Collection<? extends Thread> threads) {
UncaughtHandler handler = new UncaughtHandler();
for(Thread t : threads) {
t.setUncaughtExceptionHandler(handler);
t.start();
}
return handler;
}
public static void join(UncaughtHandler handler, long timeoutMillis, Collection<? extends Thread> threads) {
for(Thread t : threads) {
Throwable error = null;
try {
t.join(timeoutMillis);
if(t.isAlive()) {
error = new AssertionError("Thread did not complete in timeout: " + timeoutMillis);
t.interrupt();
}
} catch(InterruptedException e) {
error = e;
}
if(error != null) {
handler.uncaughtException(t, error);
}
}
}
public static UncaughtHandler startAndJoin(Collection<? extends Thread> threads) {
return startAndJoin(DEFAULT_TIMEOUT_MILLIS, threads);
}
public static UncaughtHandler startAndJoin(long timeoutMillis, Collection<? extends Thread> threads) {
UncaughtHandler handler = start(threads);
join(handler, timeoutMillis, threads);
return handler;
}
public static void runAndCheck(Collection<? extends Thread> threads) {
runAndCheck(DEFAULT_TIMEOUT_MILLIS, threads);
}
public static void runAndCheck(long timeoutMillis, Collection<? extends Thread> threads) {
UncaughtHandler handler = startAndJoin(timeoutMillis, threads);
assertEquals("Thread errors", new UncaughtHandler().toString(), handler.toString());
}
}