diff --git a/.gitignore b/.gitignore index cadf2e553..653ba488e 100644 --- a/.gitignore +++ b/.gitignore @@ -74,3 +74,5 @@ BUILDS/ # Version-content compat lane worktree. Generated, never source. .compatwt/ + +.worktrees/ diff --git a/core/src/main/java/art/arcane/iris/engine/mantle/MatterGenerator.java b/core/src/main/java/art/arcane/iris/engine/mantle/MatterGenerator.java index 0ae2d9bdf..0f53e9d6c 100644 --- a/core/src/main/java/art/arcane/iris/engine/mantle/MatterGenerator.java +++ b/core/src/main/java/art/arcane/iris/engine/mantle/MatterGenerator.java @@ -484,7 +484,11 @@ public interface MatterGenerator { @Override public boolean isReleasable() { - return task.future().isDone(); + if (!task.future().isDone()) { + return false; + } + task.future().join(); + return true; } } diff --git a/core/src/test/java/art/arcane/iris/engine/mantle/MatterGeneratorConcurrencyTest.java b/core/src/test/java/art/arcane/iris/engine/mantle/MatterGeneratorConcurrencyTest.java index 6d887a849..86b85cf48 100644 --- a/core/src/test/java/art/arcane/iris/engine/mantle/MatterGeneratorConcurrencyTest.java +++ b/core/src/test/java/art/arcane/iris/engine/mantle/MatterGeneratorConcurrencyTest.java @@ -21,8 +21,11 @@ import art.arcane.iris.util.common.parallel.MultiBurst; import java.util.List; import java.util.Set; +import java.util.concurrent.CancellationException; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; @@ -215,6 +218,37 @@ public class MatterGeneratorConcurrencyTest { assertFalse(fixture.mantle.getChunk(0, 0).isFlagged(MantleFlag.PLANNED)); } + @Test + public void dispatcherPropagatesAnAlreadyFailedSharedComponent() { + IllegalStateException failure = new IllegalStateException("Shared component failed"); + ExecutionException observed = sharedComponentFailure(CompletableFuture.failedFuture(failure), ExecutionException.class); + assertSame(failure, observed.getCause()); + } + + @Test + public void dispatcherPropagatesAnAlreadyCancelledSharedComponent() { + CompletableFuture cancelled = new CompletableFuture<>(); + cancelled.cancel(false); + sharedComponentFailure(cancelled, CancellationException.class); + } + + private T sharedComponentFailure(CompletableFuture completion, Class expectedFailure) { + GeneratorFixture fixture = new GeneratorFixture(true); + RecordingComponent component = new RecordingComponent(ReservedFlag.OBJECT, 0, 0); + TestMatterGenerator generator = fixture.generator(List.of(new MantlePass(List.of(component), 0, 0))); + MatterGenerator.MatterTaskKey key = new MatterGenerator.MatterTaskKey(fixture.mantle, 0, 0, ReservedFlag.OBJECT); + MatterGenerator.IN_FLIGHT_COMPONENTS.put(key, completion); + try { + CompletableFuture generation = MultiBurst.burst.completeValueAsync(() -> { + generator.generateMatter(0, 0, true, fixture.context); + return null; + }); + return assertThrows(expectedFailure, () -> generation.get(5L, TimeUnit.SECONDS)); + } finally { + MatterGenerator.IN_FLIGHT_COMPONENTS.remove(key, completion); + } + } + private static void await(CountDownLatch latch) { try { assertTrue(latch.await(5, TimeUnit.SECONDS));