Skip to content

Commit

Permalink
Avoid interrupting threads when rpsThreadGroup ends execution
Browse files Browse the repository at this point in the history
  • Loading branch information
rabelenda-abstracta committed May 20, 2024
1 parent 4cb19b1 commit c89272c
Showing 1 changed file with 43 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package us.abstracta.jmeter.javadsl.core.threadgroups;

import com.blazemeter.jmeter.threads.AbstractDynamicThreadGroup;
import com.blazemeter.jmeter.threads.DynamicThread;
import com.blazemeter.jmeter.threads.concurrency.ConcurrencyThreadGroup;
import com.blazemeter.jmeter.threads.concurrency.ConcurrencyThreadGroupGui;
import java.time.Duration;
Expand All @@ -16,6 +17,7 @@
import org.apache.jmeter.testelement.TestElement;
import org.apache.jmeter.testelement.property.CollectionProperty;
import org.apache.jmeter.threads.AbstractThreadGroup;
import org.apache.jmeter.threads.JMeterContextService;
import org.apache.jorphan.collections.HashTree;
import us.abstracta.jmeter.javadsl.core.BuildTreeContext;
import us.abstracta.jmeter.javadsl.core.util.JmeterFunction;
Expand Down Expand Up @@ -256,12 +258,28 @@ private TestElement buildTestAction() {
}

private TestElement buildTimer() {
VariableThroughputTimer ret = new VariableThroughputTimer();
VariableThroughputTimer ret = new NonInterruptingVariableThroughputTimer();
ret.setData(buildTimerSchedulesData());
configureTestElement(ret, buildTimerName(timerId++), VariableThroughputTimerGui.class);
return ret;
}

/**
* Always stops thread group gracefully, avoiding potential exceptions generated by
* {@link VariableThroughputTimer} when stopping a test, due to thread interruptions.
* <p>
* <a href="https://github.com/abstracta/jmeter-java-dsl/issues/257">Here</a> are more details on
* this issue.
*/
public static class NonInterruptingVariableThroughputTimer extends VariableThroughputTimer {

@Override
protected void stopTest() {
JMeterContextService.getContext().getThreadGroup().stop();
}

}

private String buildTimerName(int id) {
return "rpsTimer" + id;
}
Expand All @@ -276,7 +294,7 @@ private CollectionProperty buildTimerSchedulesData() {

@Override
protected AbstractThreadGroup buildThreadGroup() {
ConcurrencyThreadGroup ret = new ConcurrencyThreadGroup();
ConcurrencyThreadGroup ret = new ContractComplyingConcurrencyThreadGroup();
ret.setTargetLevel(
JmeterFunction.from("__tstFeedback", buildTimerName(timerId), initThreads, maxThreads,
spareThreads));
Expand All @@ -285,6 +303,29 @@ protected AbstractThreadGroup buildThreadGroup() {
return ret;
}

/**
* This implementation of ConcurrencyThreadGroup complies with {@link AbstractThreadGroup}
* contract, and the original doesn't.
* <p>
* ConcurrencyThreadGroup stop should be graceful and is not, tellThreadsToStop should interrupt
* threads and is not.
*/
public static class ContractComplyingConcurrencyThreadGroup extends ConcurrencyThreadGroup {

@Override
public void stop() {
running = false;
threadStarter.interrupt();
threads.forEach(DynamicThread::stop);
}

@Override
public void tellThreadsToStop() {
super.stop();
}

}

@Override
public LoadTimeLine buildLoadTimeline() {
LoadTimeLine ret = new LoadTimeLine(name, counting.label + " per second");
Expand Down

0 comments on commit c89272c

Please sign in to comment.