Skip to content

Commit 10f1dbf

Browse files
committed
Make worker start at head of queue again after executing a node
1 parent d013d31 commit 10f1dbf

File tree

2 files changed

+34
-20
lines changed

2 files changed

+34
-20
lines changed

junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/WorkerThreadPoolHierarchicalTestExecutorService.java

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import static java.util.Objects.requireNonNull;
1515
import static java.util.concurrent.CompletableFuture.completedFuture;
1616
import static java.util.concurrent.TimeUnit.SECONDS;
17+
import static java.util.function.Predicate.isEqual;
1718
import static org.apiguardian.api.API.Status.EXPERIMENTAL;
1819
import static org.junit.platform.commons.util.ExceptionUtils.throwAsUncheckedException;
1920
import static org.junit.platform.engine.support.hierarchical.ExclusiveResource.GLOBAL_READ_WRITE;
@@ -45,6 +46,7 @@
4546
import java.util.function.BiFunction;
4647
import java.util.function.BooleanSupplier;
4748
import java.util.function.Consumer;
49+
import java.util.function.Predicate;
4850

4951
import org.apiguardian.api.API;
5052
import org.jspecify.annotations.Nullable;
@@ -292,7 +294,8 @@ void processQueueEntries(WorkerLease workerLease, BooleanSupplier doneCondition)
292294
}
293295

294296
private void processQueueEntries() {
295-
var queueEntriesByResult = tryToStealWorkWithoutBlocking(workQueue);
297+
var queueEntriesByResult = tryToStealWorkWithoutBlocking(workQueue,
298+
isEqual(WorkStealResult.EXECUTED_BY_THIS_WORKER));
296299
var queueModified = queueEntriesByResult.containsKey(WorkStealResult.EXECUTED_BY_THIS_WORKER) //
297300
|| queueEntriesByResult.containsKey(WorkStealResult.EXECUTED_BY_DIFFERENT_WORKER);
298301
if (queueModified) {
@@ -336,7 +339,7 @@ void invokeAll(List<? extends TestTask> testTasks) {
336339
List<TestTask> sameThreadTasks = new ArrayList<>(testTasks.size());
337340
var queueEntries = forkConcurrentChildren(testTasks, isolatedTasks::add, sameThreadTasks);
338341
executeAll(sameThreadTasks);
339-
var queueEntriesByResult = tryToStealWorkWithoutBlocking(queueEntries);
342+
var queueEntriesByResult = tryToStealWorkWithoutBlocking(queueEntries, __ -> false);
340343
tryToStealWorkWithBlocking(queueEntriesByResult);
341344
waitFor(queueEntriesByResult);
342345
executeAll(isolatedTasks);
@@ -371,10 +374,10 @@ else if (child.getExecutionMode() == SAME_THREAD) {
371374
}
372375

373376
private Map<WorkStealResult, List<WorkQueue.Entry>> tryToStealWorkWithoutBlocking(
374-
Iterable<WorkQueue.Entry> queueEntries) {
377+
Iterable<WorkQueue.Entry> queueEntries, Predicate<? super WorkStealResult> stopCondition) {
375378

376379
Map<WorkStealResult, List<WorkQueue.Entry>> queueEntriesByResult = new EnumMap<>(WorkStealResult.class);
377-
tryToStealWork(queueEntries, BlockingMode.NON_BLOCKING, queueEntriesByResult);
380+
tryToStealWork(queueEntries, BlockingMode.NON_BLOCKING, queueEntriesByResult, stopCondition);
378381
return queueEntriesByResult;
379382
}
380383

@@ -383,14 +386,18 @@ private void tryToStealWorkWithBlocking(Map<WorkStealResult, List<WorkQueue.Entr
383386
if (entriesRequiringResourceLocks == null) {
384387
return;
385388
}
386-
tryToStealWork(entriesRequiringResourceLocks, BlockingMode.BLOCKING, queueEntriesByResult);
389+
tryToStealWork(entriesRequiringResourceLocks, BlockingMode.BLOCKING, queueEntriesByResult, __ -> false);
387390
}
388391

389392
private void tryToStealWork(Iterable<WorkQueue.Entry> entries, BlockingMode blocking,
390-
Map<WorkStealResult, List<WorkQueue.Entry>> queueEntriesByResult) {
393+
Map<WorkStealResult, List<WorkQueue.Entry>> queueEntriesByResult,
394+
Predicate<? super WorkStealResult> stopCondition) {
391395
for (var entry : entries) {
392-
var state = tryToStealWork(entry, blocking);
393-
queueEntriesByResult.computeIfAbsent(state, __ -> new ArrayList<>()).add(entry);
396+
var result = tryToStealWork(entry, blocking);
397+
queueEntriesByResult.computeIfAbsent(result, __ -> new ArrayList<>()).add(entry);
398+
if (stopCondition.test(result)) {
399+
break;
400+
}
394401
}
395402
}
396403

platform-tests/src/test/java/org/junit/platform/engine/support/hierarchical/WorkerThreadPoolHierarchicalTestExecutorServiceTests.java

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@
4242
import org.jspecify.annotations.NullMarked;
4343
import org.jspecify.annotations.Nullable;
4444
import org.junit.jupiter.api.AutoClose;
45-
import org.junit.jupiter.api.RepeatedTest;
4645
import org.junit.jupiter.api.Test;
4746
import org.junit.jupiter.api.Timeout;
4847
import org.junit.jupiter.api.function.Executable;
@@ -305,19 +304,28 @@ void invokeAllQueueEntriesSkipsOverUnavailableResources() throws Exception {
305304
assertThat(child3.startTime).isAfterOrEqualTo(child4.startTime);
306305
}
307306

308-
@RepeatedTest(value = 100, failureThreshold = 1)
307+
@Test
309308
void prioritizesChildrenOfStartedContainers() throws Exception {
310-
service = new WorkerThreadPoolHierarchicalTestExecutorService(configuration(2));
309+
service = new WorkerThreadPoolHierarchicalTestExecutorService(configuration(2, 2));
311310

312-
var leavesStarted = new CountDownLatch(2);
311+
var leafSubmitted = new CountDownLatch(1);
312+
var child2AndLeafStarted = new CountDownLatch(2);
313+
314+
var leaf = new TestTaskStub(ExecutionMode.CONCURRENT, child2AndLeafStarted::countDown) //
315+
.withName("leaf").withLevel(3);
313316

314-
var child1 = new TestTaskStub(ExecutionMode.CONCURRENT, leavesStarted::await) //
317+
Executable child3Behavior = () -> {
318+
var future = requiredService().submit(leaf);
319+
leafSubmitted.countDown();
320+
child2AndLeafStarted.await();
321+
future.get();
322+
};
323+
324+
var child1 = new TestTaskStub(ExecutionMode.CONCURRENT, leafSubmitted::await) //
315325
.withName("child1").withLevel(2);
316-
var child2 = new TestTaskStub(ExecutionMode.CONCURRENT, leavesStarted::countDown) //
326+
var child2 = new TestTaskStub(ExecutionMode.CONCURRENT, child2AndLeafStarted::countDown) //
317327
.withName("child2").withLevel(2);
318-
var leaf = new TestTaskStub(ExecutionMode.CONCURRENT, leavesStarted::countDown) //
319-
.withName("leaf").withLevel(3);
320-
var child3 = new TestTaskStub(ExecutionMode.CONCURRENT, () -> requiredService().submit(leaf).get()) //
328+
var child3 = new TestTaskStub(ExecutionMode.CONCURRENT, child3Behavior) //
321329
.withType(CONTAINER).withName("child3").withLevel(2);
322330

323331
var root = new TestTaskStub(ExecutionMode.SAME_THREAD,
@@ -327,11 +335,10 @@ void prioritizesChildrenOfStartedContainers() throws Exception {
327335
service.submit(root).get();
328336

329337
root.assertExecutedSuccessfully();
330-
assertThat(List.of(child1, child2, leaf, child3)).allSatisfy(TestTaskStub::assertExecutedSuccessfully);
331-
leaf.assertExecutedSuccessfully();
338+
assertThat(List.of(root, child1, child2, leaf, child3)).allSatisfy(TestTaskStub::assertExecutedSuccessfully);
332339

333340
assertThat(leaf.startTime).isBeforeOrEqualTo(child2.startTime);
334-
assertThat(leaf.executionThread).isSameAs(child3.executionThread);
341+
assertThat(leaf.executionThread).isSameAs(child2.executionThread).isNotSameAs(child3.executionThread);
335342
}
336343

337344
@Test

0 commit comments

Comments
 (0)