|
30 | 30 | import java.util.concurrent.CopyOnWriteArrayList; |
31 | 31 | import java.util.concurrent.ExecutionException; |
32 | 32 | import java.util.concurrent.ExecutorService; |
| 33 | +import java.util.concurrent.Executors; |
33 | 34 | import java.util.concurrent.Future; |
34 | | -import java.util.concurrent.ThreadPoolExecutor; |
35 | 35 | import java.util.concurrent.TimeUnit; |
36 | 36 | import java.util.concurrent.TimeoutException; |
37 | | -import java.util.concurrent.atomic.AtomicInteger; |
| 37 | +import java.util.concurrent.locks.ReentrantLock; |
38 | 38 |
|
39 | 39 | import org.apache.jmeter.JMeter; |
40 | 40 | import org.apache.jmeter.samplers.SampleEvent; |
@@ -92,19 +92,17 @@ public class StandardJMeterEngine implements JMeterEngine, Runnable { |
92 | 92 | /** Whether to call System.exit(0) unconditionally at end of non-GUI test */ |
93 | 93 | private static final boolean SYSTEM_EXIT_FORCED = JMeterUtils.getPropDefault("jmeterengine.force.system.exit", false); |
94 | 94 |
|
95 | | - private static final AtomicInteger THREAD_COUNTER = new AtomicInteger(0); |
96 | | - |
97 | 95 | /** |
98 | 96 | * Executor service to execute management tasks like "start test", "stop test". |
99 | | - * The use of {@link ExecutorService} allows propagating the exception from the threads. |
100 | | - * Thread keepalive time is set to 1 second, so threads are released early, |
101 | | - * so the application can shut down faster. |
| 97 | + * Uses platform threads to keep the JVM alive while tests run |
| 98 | + * (virtual threads are daemon threads and would allow premature JVM exit). |
102 | 99 | */ |
103 | 100 | private static final ExecutorService EXECUTOR_SERVICE = |
104 | | - new ThreadPoolExecutor(0, Integer.MAX_VALUE, |
105 | | - 1L, TimeUnit.SECONDS, |
106 | | - new java.util.concurrent.SynchronousQueue<>(), |
107 | | - (runnable) -> new Thread(runnable, "StandardJMeterEngine-" + THREAD_COUNTER.incrementAndGet())); |
| 101 | + Executors.newThreadPerTaskExecutor( |
| 102 | + Thread.ofPlatform().name("StandardJMeterEngine-", 1).factory()); |
| 103 | + |
| 104 | + private static final ReentrantLock REGISTER_LOCK = new ReentrantLock(); |
| 105 | + private final ReentrantLock stopTestLock = new ReentrantLock(); |
108 | 106 |
|
109 | 107 | private volatile Future<?> runningTest; |
110 | 108 |
|
@@ -156,8 +154,13 @@ public static void stopEngine() { |
156 | 154 | } |
157 | 155 | } |
158 | 156 |
|
159 | | - public static synchronized void register(TestStateListener tl) { |
160 | | - testList.add(tl); |
| 157 | + public static void register(TestStateListener tl) { |
| 158 | + REGISTER_LOCK.lock(); |
| 159 | + try { |
| 160 | + testList.add(tl); |
| 161 | + } finally { |
| 162 | + REGISTER_LOCK.unlock(); |
| 163 | + } |
161 | 164 | } |
162 | 165 |
|
163 | 166 | public static boolean stopThread(String threadName) { |
@@ -296,14 +299,19 @@ public void reset() { |
296 | 299 | * Stop Test Now |
297 | 300 | */ |
298 | 301 | @Override |
299 | | - public synchronized void stopTest() { |
| 302 | + public void stopTest() { |
300 | 303 | stopTest(true); |
301 | 304 | } |
302 | 305 |
|
303 | 306 | @Override |
304 | 307 | @SuppressWarnings("FutureReturnValueIgnored") |
305 | | - public synchronized void stopTest(boolean now) { |
306 | | - EXECUTOR_SERVICE.submit(new StopTest(now)); |
| 308 | + public void stopTest(boolean now) { |
| 309 | + stopTestLock.lock(); |
| 310 | + try { |
| 311 | + EXECUTOR_SERVICE.submit(new StopTest(now)); |
| 312 | + } finally { |
| 313 | + stopTestLock.unlock(); |
| 314 | + } |
307 | 315 | } |
308 | 316 |
|
309 | 317 | private class StopTest implements Runnable { |
|
0 commit comments