Parallelized mod builds

- Mod targets run through bounded Gradle workers with separate caches and daemon slots
- Target logs retain verification failures and bounded output summaries
- Nested tests use bounded forks and JVM thread counts
- Lifecycle tests control deadlines through injected clocks
This commit is contained in:
Brian Neumann-Fopiano committed 2026-10-21 02:53:00 -04:00
1 parent d7059d84a4
commit fc810221bb
17 files changed
+447 -61

No files matched your search

@@ -30,6 +30,7 @@ import art.arcane.iris.generation.runtime.Engine;
import art.arcane.iris.modded.ModdedGenPool;
import art.arcane.volmlib.util.mantle.runtime.Mantle;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Semaphore;
@@ -39,6 +40,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.LongSupplier;
public final class ModdedPregenMethod implements PregeneratorMethod {
private static final int ADAPTIVE_TIMEOUT_STEP = 3;
@@ -68,8 +70,14 @@ public final class ModdedPregenMethod implements PregeneratorMethod {
private final int timeoutSeconds;
private final PregenMantleBackpressure backpressure;
private final PauseWhenEmptyGuard pauseGuard;
private final LongSupplier clock;
public ModdedPregenMethod(Configuration configuration) {
this(configuration, System::nanoTime);
}
ModdedPregenMethod(Configuration configuration, LongSupplier clock) {
this.clock = Objects.requireNonNull(clock);
runtime = configuration.runtime();
engine = configuration.engine();
sync = configuration.sync();
@@ -178,7 +186,7 @@ public final class ModdedPregenMethod implements PregeneratorMethod {
if (sync || runtime.isServerThread()) {
return;
}
long deadline = System.nanoTime() + TimeUnit.SECONDS.toNanos(timeoutSeconds)
long deadline = clock.getAsLong() + TimeUnit.SECONDS.toNanos(timeoutSeconds)
+ TimeUnit.MILLISECONDS.toNanos(FINAL_SAVE_TIMEOUT_MILLIS);
boolean interrupted = false;
try {
@@ -186,7 +194,7 @@ public final class ModdedPregenMethod implements PregeneratorMethod {
if (finalSaveOnServerThread.get() || !runtime.isRunning()) {
return;
}
if (System.nanoTime() >= deadline) {
if (clock.getAsLong() >= deadline) {
throw new IllegalStateException("Iris pregen chunk callbacks did not finish for "
+ runtime.worldIdentity() + ": " + inFlight.get() + " pending");
}
@@ -263,14 +271,14 @@ public final class ModdedPregenMethod implements PregeneratorMethod {
}
private void awaitFinalSave(FinalSaveRequest request) {
long deadline = System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(FINAL_SAVE_TIMEOUT_MILLIS);
long deadline = clock.getAsLong() + TimeUnit.MILLISECONDS.toNanos(FINAL_SAVE_TIMEOUT_MILLIS);
boolean interrupted = false;
try {
while (true) {
if (deferFinalSaveIfRequested()) {
return;
}
long remainingNanos = deadline - System.nanoTime();
long remainingNanos = deadline - clock.getAsLong();
if (remainingNanos <= 0L) {
ModdedIrisLog.warn("Iris pregen level save did not complete in time for {}", runtime.worldIdentity());
return;
@@ -21,6 +21,7 @@ import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
import static org.junit.Assert.assertEquals;
@@ -97,6 +98,9 @@ public class ModdedPregenMethodLifecycleTest {
@Test
public void finalSaveWaitsForEveryCallbackBeyondFiveSeconds() throws Exception {
AtomicLong clock = new AtomicLong();
CountDownLatch started = new CountDownLatch(1);
CountDownLatch advanced = new CountDownLatch(2);
CompletableFuture<ChunkLoad> load = new CompletableFuture<>();
CompletableFuture<ChunkLoad> failedLoad = new CompletableFuture<>();
PregenListener listener = mock(PregenListener.class);
@@ -110,16 +114,22 @@ public class ModdedPregenMethodLifecycleTest {
}).when(runtime).execute(any());
try (MockedStatic<MantleHeapPressure> pressure = mockStatic(MantleHeapPressure.class)) {
pressure.when(MantleHeapPressure::memoryBudgetBytes).thenReturn(512L << 20);
method = new ModdedPregenMethod(new ModdedPregenMethod.Configuration(runtime, engine, false));
method = new ModdedPregenMethod(new ModdedPregenMethod.Configuration(runtime, engine, false), () -> {
long now = clock.get();
if (now >= TimeUnit.SECONDS.toNanos(6)) {
advanced.countDown();
} else {
started.countDown();
}
return now;
});
method.generateChunk(2, 3, listener);
method.generateChunk(4, 5, listener);
CountDownLatch started = new CountDownLatch(1);
Future<?> closing = executor.submit(() -> {
started.countDown();
method.close();
});
Future<?> closing = executor.submit(method::close);
assertTrue(started.await(5, TimeUnit.SECONDS));
assertThrows(TimeoutException.class, () -> closing.get(6, TimeUnit.SECONDS));
clock.set(TimeUnit.SECONDS.toNanos(6));
assertTrue(advanced.await(5, TimeUnit.SECONDS));
assertFalse(closing.isDone());
verify(runtime, never()).save();
verify(engine, never()).syncGenerationHistory();
load.complete(new ChunkLoad(true, null));