Skip to content

Commit c89272c

Browse files
Avoid interrupting threads when rpsThreadGroup ends execution
1 parent 4cb19b1 commit c89272c

File tree

1 file changed

+43
-2
lines changed

1 file changed

+43
-2
lines changed

jmeter-java-dsl/src/main/java/us/abstracta/jmeter/javadsl/core/threadgroups/RpsThreadGroup.java

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package us.abstracta.jmeter.javadsl.core.threadgroups;
22

33
import com.blazemeter.jmeter.threads.AbstractDynamicThreadGroup;
4+
import com.blazemeter.jmeter.threads.DynamicThread;
45
import com.blazemeter.jmeter.threads.concurrency.ConcurrencyThreadGroup;
56
import com.blazemeter.jmeter.threads.concurrency.ConcurrencyThreadGroupGui;
67
import java.time.Duration;
@@ -16,6 +17,7 @@
1617
import org.apache.jmeter.testelement.TestElement;
1718
import org.apache.jmeter.testelement.property.CollectionProperty;
1819
import org.apache.jmeter.threads.AbstractThreadGroup;
20+
import org.apache.jmeter.threads.JMeterContextService;
1921
import org.apache.jorphan.collections.HashTree;
2022
import us.abstracta.jmeter.javadsl.core.BuildTreeContext;
2123
import us.abstracta.jmeter.javadsl.core.util.JmeterFunction;
@@ -256,12 +258,28 @@ private TestElement buildTestAction() {
256258
}
257259

258260
private TestElement buildTimer() {
259-
VariableThroughputTimer ret = new VariableThroughputTimer();
261+
VariableThroughputTimer ret = new NonInterruptingVariableThroughputTimer();
260262
ret.setData(buildTimerSchedulesData());
261263
configureTestElement(ret, buildTimerName(timerId++), VariableThroughputTimerGui.class);
262264
return ret;
263265
}
264266

267+
/**
268+
* Always stops thread group gracefully, avoiding potential exceptions generated by
269+
* {@link VariableThroughputTimer} when stopping a test, due to thread interruptions.
270+
* <p>
271+
* <a href="https://github.com/abstracta/jmeter-java-dsl/issues/257">Here</a> are more details on
272+
* this issue.
273+
*/
274+
public static class NonInterruptingVariableThroughputTimer extends VariableThroughputTimer {
275+
276+
@Override
277+
protected void stopTest() {
278+
JMeterContextService.getContext().getThreadGroup().stop();
279+
}
280+
281+
}
282+
265283
private String buildTimerName(int id) {
266284
return "rpsTimer" + id;
267285
}
@@ -276,7 +294,7 @@ private CollectionProperty buildTimerSchedulesData() {
276294

277295
@Override
278296
protected AbstractThreadGroup buildThreadGroup() {
279-
ConcurrencyThreadGroup ret = new ConcurrencyThreadGroup();
297+
ConcurrencyThreadGroup ret = new ContractComplyingConcurrencyThreadGroup();
280298
ret.setTargetLevel(
281299
JmeterFunction.from("__tstFeedback", buildTimerName(timerId), initThreads, maxThreads,
282300
spareThreads));
@@ -285,6 +303,29 @@ protected AbstractThreadGroup buildThreadGroup() {
285303
return ret;
286304
}
287305

306+
/**
307+
* This implementation of ConcurrencyThreadGroup complies with {@link AbstractThreadGroup}
308+
* contract, and the original doesn't.
309+
* <p>
310+
* ConcurrencyThreadGroup stop should be graceful and is not, tellThreadsToStop should interrupt
311+
* threads and is not.
312+
*/
313+
public static class ContractComplyingConcurrencyThreadGroup extends ConcurrencyThreadGroup {
314+
315+
@Override
316+
public void stop() {
317+
running = false;
318+
threadStarter.interrupt();
319+
threads.forEach(DynamicThread::stop);
320+
}
321+
322+
@Override
323+
public void tellThreadsToStop() {
324+
super.stop();
325+
}
326+
327+
}
328+
288329
@Override
289330
public LoadTimeLine buildLoadTimeline() {
290331
LoadTimeLine ret = new LoadTimeLine(name, counting.label + " per second");

0 commit comments

Comments
 (0)