use of com.carrotsearch.randomizedtesting.annotations.ThreadLeakAction.Action in project randomizedtesting by randomizedtesting.
the class ThreadLeakControl method checkThreadLeaks.
/**
* Perform a thread leak check at the given scope.
*/
@SuppressWarnings("deprecation")
protected void checkThreadLeaks(Set<Thread> expectedState, List<Throwable> errors, LifecycleScope scope, Description description, AnnotatedElement... annotationChain) {
final ThreadLeakScope annScope = firstAnnotated(ThreadLeakScope.class, annotationChain);
// Return immediately if no checking.
if (annScope.value() == Scope.NONE)
return;
// If suite scope check is requested skip testing at test level.
if (annScope.value() == Scope.SUITE && scope == LifecycleScope.TEST) {
return;
}
// Check for the set of live threads, with optional lingering.
int lingerTime = firstAnnotated(ThreadLeakLingering.class, annotationChain).linger();
HashSet<Thread> threads = getThreads(suiteFilters);
threads.removeAll(expectedState);
if (lingerTime > 0 && !threads.isEmpty()) {
final long deadline = System.currentTimeMillis() + lingerTime;
try {
LOGGER.warning("Will linger awaiting termination of " + threads.size() + " leaked thread(s).");
do {
// Check every few hundred milliseconds until deadline occurs. We want to break out
// sooner than the maximum lingerTime but there is no explicit even that
// would wake us up, so poll periodically.
Thread.sleep(250);
threads = getThreads(suiteFilters);
threads.removeAll(expectedState);
if (threads.isEmpty() || System.currentTimeMillis() > deadline)
break;
} while (true);
} catch (InterruptedException e) {
LOGGER.warning("Lingering interrupted.");
}
}
if (threads.isEmpty()) {
return;
}
// Take one more snapshot, this time including stack traces (costly).
HashMap<Thread, StackTraceElement[]> withTraces = getThreadsWithTraces(suiteFilters);
withTraces.keySet().removeAll(expectedState);
if (withTraces.isEmpty()) {
return;
}
// Build up failure message (include stack traces of leaked threads).
StringBuilder message = new StringBuilder(threads.size() + " thread" + (threads.size() == 1 ? "" : "s") + " leaked from " + scope + " scope at " + description + ": ");
message.append(formatThreadStacks(withTraces));
// The first exception is leaked threads error.
errors.add(RandomizedRunner.augmentStackTrace(emptyStack(new ThreadLeakError(message.toString()))));
// Perform actions on leaked threads.
final EnumSet<Action> actions = EnumSet.noneOf(Action.class);
actions.addAll(Arrays.asList(firstAnnotated(ThreadLeakAction.class, annotationChain).value()));
if (actions.contains(Action.WARN)) {
LOGGER.severe(message.toString());
}
Set<Thread> zombies = Collections.emptySet();
if (actions.contains(Action.INTERRUPT)) {
zombies = tryToInterruptAll(errors, withTraces.keySet());
}
// Process zombie thread check consequences here.
if (!zombies.isEmpty()) {
switch(firstAnnotated(ThreadLeakZombies.class, annotationChain).value()) {
case CONTINUE:
// Do nothing about it.
break;
case IGNORE_REMAINING_TESTS:
// Mark zombie thread presence.
RandomizedRunner.zombieMarker.set(true);
break;
default:
throw new RuntimeException("Missing case.");
}
}
}
Aggregations