Mantle window helping

- A caller whose submission window is full runs the next component itself instead of parking
- Pass barriers run still-queued component tasks in reverse order before waiting
- Queued component tasks reopen the submitting generation's runtime scope on whichever thread runs them
- Helpers only run queued tasks bound to their own runtime or when they hold none
- A finished submission no longer reports a task as dropped once a helper started it
- Burst worker scope test keeps one stage on a worker while the caller helps
This commit is contained in:
Brian Neumann-Fopiano committed 2026-10-22 02:56:23 -04:00
1 parent 024214d7f0
commit e91eb258aa
3 files changed
+351 -53

No files matched your search

@@ -3,6 +3,7 @@ package art.arcane.iris.generation.mantle;
import art.arcane.iris.generation.runtime.IrisComplex;
import art.arcane.iris.generation.runtime.Engine;
import art.arcane.iris.generation.runtime.EnginePlatformHooks;
import art.arcane.iris.generation.runtime.IrisEngine;
import art.arcane.iris.spi.IrisLogging;
import art.arcane.iris.generation.concurrent.MultiBurst;
import art.arcane.iris.generation.context.ChunkContext;
@@ -106,6 +107,7 @@ public interface MatterGenerator {
// write into released chunks.
List<MatterComponentTask> outstandingTasks = null;
List<DeferredComponent> deferredComponents = null;
IrisEngine.GenerationRuntimeBinding generationRuntime = scopedGenerationRuntime();
SubmissionWindow submissions = multicore && !DISPATCHER.ownsCurrentThread()
? new SubmissionWindow()
: null;
@@ -231,7 +233,8 @@ public interface MatterGenerator {
for (int componentIndex = 0; componentIndex < eligibleComponentCount; componentIndex++) {
MantleComponent component = eligibleComponents[componentIndex];
outstandingTasks.add(runComponentAsync(
chunk, component, writer, finalPassX, finalPassZ, context, submissions));
chunk, component, writer, finalPassX, finalPassZ, context, submissions,
generationRuntime));
}
} else {
for (int componentIndex = 0; componentIndex < eligibleComponentCount; componentIndex++) {
@@ -248,6 +251,7 @@ public interface MatterGenerator {
}
if (asyncComponents) {
runQueuedComponentTasks(outstandingTasks, generationRuntime);
awaitComponentTasks(outstandingTasks);
} else if (deferredComponents != null) {
runDeferredComponents(deferredComponents, writer, context);
@@ -382,6 +386,19 @@ public interface MatterGenerator {
return true;
}
private IrisEngine.GenerationRuntimeBinding scopedGenerationRuntime() {
return getEngine() instanceof IrisEngine irisEngine && irisEngine.hasGenerationRuntimeScope()
? irisEngine.captureGenerationRuntimeBinding()
: null;
}
private IrisEngine.GenerationRuntimeScope openGenerationRuntime(IrisEngine.GenerationRuntimeBinding binding) {
if (binding == null || !(getEngine() instanceof IrisEngine irisEngine)) {
return null;
}
return irisEngine.openGenerationRuntimeScope(binding);
}
private boolean shouldGenerateComponent(MantleComponent component) {
if (!component.isEnabled()) {
return false;
@@ -398,7 +415,8 @@ public interface MatterGenerator {
int chunkX,
int chunkZ,
ChunkContext context,
SubmissionWindow submissions
SubmissionWindow submissions,
IrisEngine.GenerationRuntimeBinding generationRuntime
) {
MantleFlag flag = component.getFlag();
MatterTaskKey key = new MatterTaskKey(getMantle(), chunkX, chunkZ, flag);
@@ -428,24 +446,25 @@ public interface MatterGenerator {
if (running != null) {
return running;
}
submissions.acquire();
boolean submit = submissions.reserve();
MatterComponentTask existing = IN_FLIGHT_COMPONENTS.putIfAbsent(key, task);
if (existing != null) {
submissions.release();
if (submit) {
submissions.release();
}
return existing;
}
try {
if (!submit) {
completeComponentTask(task, chunk, component, writer, chunkX, chunkZ, context, null);
return task;
}
IrisContext callerContext = IrisContext.get();
submissions.track(task);
task.setSubmission(DISPATCHER.submit(() -> completeComponentTask(
task,
chunk,
component,
writer,
chunkX,
chunkZ,
context,
callerContext)));
Runnable work = () -> runComponentTask(
task, chunk, component, writer, chunkX, chunkZ, context, callerContext, generationRuntime);
task.queue(work, generationRuntime);
task.setSubmission(DISPATCHER.submit(work));
return task;
} catch (Throwable throwable) {
task.cancel(throwable);
@@ -489,6 +508,20 @@ public interface MatterGenerator {
deferredComponents.clear();
}
private static void runQueuedComponentTasks(List<MatterComponentTask> tasks,
IrisEngine.GenerationRuntimeBinding helperRuntime) {
boolean interrupted = Thread.interrupted();
try {
for (int index = tasks.size() - 1; index >= 0; index--) {
tasks.get(index).runIfQueued(helperRuntime);
}
} finally {
if (interrupted) {
Thread.currentThread().interrupt();
}
}
}
/**
* Pass barrier. Waits for every launched task, even if one of them failed, so that no task is
* still writing through the writer once this returns. The first failure is rethrown.
@@ -592,19 +625,42 @@ public interface MatterGenerator {
int chunkZ,
ChunkContext context,
IrisContext callerContext
) {
Throwable failure = runComponentTask(task, chunk, component, writer, chunkX, chunkZ, context, callerContext, null);
if (failure instanceof RuntimeException runtime) {
throw runtime;
}
if (failure instanceof Error error) {
throw error;
}
if (failure != null) {
throw new CompletionException(failure);
}
}
private Throwable runComponentTask(
MatterComponentTask task,
MantleChunk<Matter> chunk,
MantleComponent component,
MantleWriter writer,
int chunkX,
int chunkZ,
ChunkContext context,
IrisContext callerContext,
IrisEngine.GenerationRuntimeBinding generationRuntime
) {
if (!task.start()) {
return;
return null;
}
Throwable failure = null;
try {
runComponentWithContext(chunk, component, writer, chunkX, chunkZ, context, callerContext);
runComponentWithContext(chunk, component, writer, chunkX, chunkZ, context, callerContext, generationRuntime);
} catch (Throwable throwable) {
failure = throwable;
throw throwable;
} finally {
task.finish(failure);
}
return failure;
}
private void runComponentWithContext(
@@ -614,17 +670,19 @@ public interface MatterGenerator {
int chunkX,
int chunkZ,
ChunkContext context,
IrisContext callerContext
IrisContext callerContext,
IrisEngine.GenerationRuntimeBinding generationRuntime
) {
if (callerContext == null) {
if (callerContext == null && generationRuntime == null) {
runComponentInline(chunk, component, writer, chunkX, chunkZ, context);
return;
}
try (IrisContext.Scope ignored = IrisContext.open(
callerContext.getEngine(),
callerContext.getGenerationSessionId(),
callerContext.getChunkContext())) {
try (IrisEngine.GenerationRuntimeScope runtimeScope = openGenerationRuntime(generationRuntime);
IrisContext.Scope contextScope = callerContext == null ? null : IrisContext.open(
callerContext.getEngine(),
callerContext.getGenerationSessionId(),
callerContext.getChunkContext())) {
runComponentInline(chunk, component, writer, chunkX, chunkZ, context);
}
}
@@ -647,6 +705,8 @@ public interface MatterGenerator {
private final MatterTaskKey key;
private final CompletableFuture<Void> future = new CompletableFuture<>();
private volatile Future<?> submission;
private volatile Runnable queuedWork;
private volatile IrisEngine.GenerationRuntimeBinding queuedRuntime;
private volatile Throwable failure;
private boolean started;
private boolean slowWarningIssued;
@@ -667,6 +727,22 @@ public interface MatterGenerator {
return true;
}
synchronized boolean isStarted() {
return started;
}
void queue(Runnable work, IrisEngine.GenerationRuntimeBinding runtime) {
queuedRuntime = runtime;
queuedWork = work;
}
void runIfQueued(IrisEngine.GenerationRuntimeBinding helperRuntime) {
Runnable work = queuedWork;
if (work != null && !future.isDone() && (helperRuntime == null || helperRuntime == queuedRuntime)) {
work.run();
}
}
synchronized void setSubmission(Future<?> submission) {
this.submission = submission;
if (failure != null) {
@@ -720,7 +796,7 @@ public interface MatterGenerator {
continue;
}
Future<?> submitted = submission;
boolean dropped = submitted != null && submitted.isDone();
boolean dropped = submitted != null && submitted.isDone() && !isStarted();
long waited = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);
if (dropped) {
IllegalStateException dispatchFailure = new IllegalStateException(
@@ -775,13 +851,20 @@ public interface MatterGenerator {
private final Semaphore slots = new Semaphore(COMPONENT_SUBMISSION_LIMIT);
private volatile Throwable failure;
void acquire() {
slots.acquireUninterruptibly();
boolean reserve() {
Throwable observed = failure;
if (observed != null) {
throw new CompletionException(observed);
}
if (!slots.tryAcquire()) {
return false;
}
observed = failure;
if (observed != null) {
slots.release();
throw new CompletionException(observed);
}
return true;
}
void release() {