Skip to content

Commit ea2be9e

Browse files
elrodro83kcooney
authored andcommitted
ThreadGroup instance leaked when using Timeout rule (junit-team#1517)
1 parent 456aee3 commit ea2be9e

File tree

2 files changed

+44
-6
lines changed

2 files changed

+44
-6
lines changed

src/main/java/org/junit/internal/runners/statements/FailOnTimeout.java

+20-6
Original file line numberDiff line numberDiff line change
@@ -122,12 +122,26 @@ public void evaluate() throws Throwable {
122122
FutureTask<Throwable> task = new FutureTask<Throwable>(callable);
123123
ThreadGroup threadGroup = new ThreadGroup("FailOnTimeoutGroup");
124124
Thread thread = new Thread(threadGroup, task, "Time-limited test");
125-
thread.setDaemon(true);
126-
thread.start();
127-
callable.awaitStarted();
128-
Throwable throwable = getResult(task, thread);
129-
if (throwable != null) {
130-
throw throwable;
125+
try {
126+
thread.setDaemon(true);
127+
thread.start();
128+
callable.awaitStarted();
129+
Throwable throwable = getResult(task, thread);
130+
if (throwable != null) {
131+
throw throwable;
132+
}
133+
} finally {
134+
try {
135+
thread.join(1);
136+
} catch (InterruptedException e) {
137+
Thread.currentThread().interrupt();
138+
}
139+
try {
140+
threadGroup.destroy();
141+
} catch (IllegalThreadStateException e) {
142+
// If a thread from the group is still alive, the ThreadGroup cannot be destroyed.
143+
// Swallow the exception to keep the same behavior prior to this change.
144+
}
131145
}
132146
}
133147

src/test/java/org/junit/internal/runners/statements/FailOnTimeoutTest.java

+24
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import static java.lang.Long.MAX_VALUE;
44
import static java.lang.Math.atan;
55
import static java.lang.System.currentTimeMillis;
6+
import static java.lang.Thread.currentThread;
67
import static java.lang.Thread.sleep;
78
import static java.util.concurrent.TimeUnit.MILLISECONDS;
89
import static org.junit.Assert.assertEquals;
@@ -13,6 +14,10 @@
1314
import static org.junit.Assert.fail;
1415
import static org.junit.internal.runners.statements.FailOnTimeout.builder;
1516

17+
import java.util.Arrays;
18+
import java.util.Collection;
19+
import java.util.HashSet;
20+
import java.util.Set;
1621
import java.util.concurrent.TimeUnit;
1722

1823
import org.junit.Test;
@@ -202,4 +207,23 @@ private void notTheRealCauseOfTheTimeout() {
202207
}
203208
}
204209
}
210+
211+
@Test
212+
public void threadGroupNotLeaked() throws Throwable {
213+
Collection<ThreadGroup> groupsBeforeSet = subGroupsOfCurrentThread();
214+
215+
evaluateWithWaitDuration(0);
216+
217+
for (ThreadGroup group: subGroupsOfCurrentThread()) {
218+
if (!groupsBeforeSet.contains(group) && "FailOnTimeoutGroup".equals(group.getName())) {
219+
fail("A 'FailOnTimeoutGroup' thread group remains referenced after the test execution.");
220+
}
221+
}
222+
}
223+
224+
private Collection<ThreadGroup> subGroupsOfCurrentThread() {
225+
ThreadGroup[] subGroups = new ThreadGroup[256];
226+
int numGroups = currentThread().getThreadGroup().enumerate(subGroups);
227+
return Arrays.asList(subGroups).subList(0, numGroups);
228+
}
205229
}

0 commit comments

Comments
 (0)