mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-10-12 03:24:14 +00:00
Retained active generation resources
- Failed planner drains retain mantle and generation data for retry - Detached runtime cleanup retains its target until runtime teardown succeeds - Terrain caches clear after hydrology shutdown completes
This commit is contained in:
@@ -192,8 +192,17 @@ final class EngineShutdownSequence {
|
||||
() -> NativeStructureOwnershipStore.close(engine));
|
||||
cleanupFailure = appendFailure(cleanupFailure, ownershipFailure);
|
||||
if (ownershipFailure == null) {
|
||||
cleanupFailure = closeDetachedGenerationRuntimes(cleanupFailure);
|
||||
cleanupFailure = closeRuntime(engine.runtime, cleanupFailure);
|
||||
Throwable runtimeFailure = closeDetachedGenerationRuntimes(null);
|
||||
if (runtimeFailure == null) {
|
||||
runtimeFailure = closeRuntime(engine.runtime, null);
|
||||
}
|
||||
cleanupFailure = appendFailure(cleanupFailure, runtimeFailure);
|
||||
if (runtimeFailure != null) {
|
||||
if (cleanupFailure != original) {
|
||||
original.addSuppressed(cleanupFailure);
|
||||
}
|
||||
return;
|
||||
}
|
||||
engine.runtime = null;
|
||||
cleanupFailure = runCleanup(cleanupFailure, engine.publishedTarget::close);
|
||||
cleanupFailure = runCleanup(cleanupFailure, engine.engineDataStore::releaseEngineData);
|
||||
@@ -229,11 +238,15 @@ final class EngineShutdownSequence {
|
||||
assembly.mode.close();
|
||||
}
|
||||
});
|
||||
failure = runCleanup(failure, () -> {
|
||||
Throwable complexFailure = runCleanup(null, () -> {
|
||||
if (assembly.complex != null) {
|
||||
assembly.complex.close();
|
||||
}
|
||||
});
|
||||
failure = appendFailure(failure, complexFailure);
|
||||
if (complexFailure != null) {
|
||||
return failure;
|
||||
}
|
||||
failure = runCleanup(failure, () -> {
|
||||
if (assembly.hash32 != null) {
|
||||
assembly.hash32.cancel(true);
|
||||
@@ -277,7 +290,11 @@ final class EngineShutdownSequence {
|
||||
return failure;
|
||||
}
|
||||
failure = runCleanup(failure, generationRuntime.mode()::close);
|
||||
failure = runCleanup(failure, generationRuntime.complex()::close);
|
||||
Throwable complexFailure = runCleanup(null, generationRuntime.complex()::close);
|
||||
failure = appendFailure(failure, complexFailure);
|
||||
if (complexFailure != null) {
|
||||
return failure;
|
||||
}
|
||||
failure = runCleanup(failure, () -> generationRuntime.hash32().cancel(true));
|
||||
if (generationRuntime.mantle() != retainedMantle) {
|
||||
failure = runCleanup(failure, generationRuntime.mantle()::saveAllNow);
|
||||
@@ -290,10 +307,14 @@ final class EngineShutdownSequence {
|
||||
IrisEngine.GenerationRuntimeBinding binding = new IrisEngine.GenerationRuntimeBinding(
|
||||
engine,
|
||||
generationRuntime);
|
||||
Throwable runtimeFailure;
|
||||
try (IrisEngine.GenerationRuntimeScope ignored = engine.generationRuntimeScopes.open(binding)) {
|
||||
failure = closeGenerationRuntime(generationRuntime, failure);
|
||||
runtimeFailure = closeGenerationRuntime(generationRuntime, null);
|
||||
} catch (Throwable scopeFailure) {
|
||||
failure = appendFailure(failure, scopeFailure);
|
||||
runtimeFailure = scopeFailure;
|
||||
}
|
||||
if (runtimeFailure != null) {
|
||||
return appendFailure(failure, runtimeFailure);
|
||||
}
|
||||
return closeDetachedTarget(generationRuntime.target(), failure);
|
||||
}
|
||||
|
||||
@@ -1783,9 +1783,9 @@ public class IrisComplex implements DataProvider {
|
||||
}
|
||||
|
||||
public void close() {
|
||||
resolvedTerrain.clear();
|
||||
if (hydrologyRuntime != null) {
|
||||
hydrologyRuntime.close();
|
||||
}
|
||||
resolvedTerrain.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
package art.arcane.iris.engine;
|
||||
|
||||
import art.arcane.iris.engine.EngineRuntimeBuilder.RuntimeAssembly;
|
||||
import art.arcane.iris.engine.framework.EngineMode;
|
||||
import art.arcane.iris.engine.mantle.EngineMantle;
|
||||
import art.arcane.iris.spi.IrisPlatform;
|
||||
import art.arcane.iris.spi.IrisPlatforms;
|
||||
import art.arcane.iris.spi.PlatformBlockState;
|
||||
import art.arcane.iris.spi.PlatformRegistries;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.mockito.InOrder;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.Mockito.doThrow;
|
||||
import static org.mockito.Mockito.inOrder;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verifyNoInteractions;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
public class EngineShutdownDrainTest {
|
||||
private static IrisPlatform previousPlatform;
|
||||
|
||||
@BeforeClass
|
||||
public static void bindPlatform() {
|
||||
previousPlatform = IrisPlatforms.isBound() ? IrisPlatforms.get() : null;
|
||||
IrisPlatforms.unbind();
|
||||
IrisPlatform platform = mock(IrisPlatform.class);
|
||||
PlatformRegistries registries = mock(PlatformRegistries.class);
|
||||
when(platform.registries()).thenReturn(registries);
|
||||
when(registries.block(anyString())).thenReturn(mock(PlatformBlockState.class));
|
||||
IrisPlatforms.bind(platform);
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void restorePlatform() {
|
||||
IrisPlatforms.unbind();
|
||||
if (previousPlatform != null) {
|
||||
IrisPlatforms.bind(previousPlatform);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void failedPlannerDrainRetainsRuntimeStorage() {
|
||||
GenerationRuntime runtime = mock(GenerationRuntime.class);
|
||||
IrisComplex complex = mock(IrisComplex.class);
|
||||
EngineMantle mantle = mock(EngineMantle.class);
|
||||
CompletableFuture<Long> hash = new CompletableFuture<>();
|
||||
when(runtime.mode()).thenReturn(mock(EngineMode.class));
|
||||
when(runtime.complex()).thenReturn(complex);
|
||||
when(runtime.mantle()).thenReturn(mantle);
|
||||
when(runtime.hash32()).thenReturn(hash);
|
||||
IllegalStateException failure = new IllegalStateException("Planner still active");
|
||||
doThrow(failure).when(complex).close();
|
||||
|
||||
assertSame(failure, new EngineShutdownSequence(null).closeGenerationRuntime(runtime, null));
|
||||
verifyNoInteractions(mantle);
|
||||
assertFalse(hash.isCancelled());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void successfulPlannerDrainPrecedesMantleRelease() {
|
||||
GenerationRuntime runtime = mock(GenerationRuntime.class);
|
||||
IrisComplex complex = mock(IrisComplex.class);
|
||||
EngineMantle mantle = mock(EngineMantle.class);
|
||||
CompletableFuture<Long> hash = new CompletableFuture<>();
|
||||
when(runtime.mode()).thenReturn(mock(EngineMode.class));
|
||||
when(runtime.complex()).thenReturn(complex);
|
||||
when(runtime.mantle()).thenReturn(mantle);
|
||||
when(runtime.hash32()).thenReturn(hash);
|
||||
|
||||
assertNull(new EngineShutdownSequence(null).closeGenerationRuntime(runtime, null));
|
||||
InOrder releases = inOrder(complex, mantle);
|
||||
releases.verify(complex).close();
|
||||
releases.verify(mantle).saveAllNow();
|
||||
releases.verify(mantle).close();
|
||||
assertTrue(hash.isCancelled());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void failedAssemblyDrainRetainsMantleOwnership() {
|
||||
RuntimeAssembly assembly = mock(RuntimeAssembly.class);
|
||||
assembly.complex = mock(IrisComplex.class);
|
||||
assembly.mantle = mock(EngineMantle.class);
|
||||
assembly.ownsMantle = true;
|
||||
assembly.hash32 = new CompletableFuture<>();
|
||||
IllegalStateException failure = new IllegalStateException("Planner still active");
|
||||
doThrow(failure).when(assembly.complex).close();
|
||||
|
||||
assertSame(failure, new EngineShutdownSequence(null).closeAssembly(assembly, null));
|
||||
verifyNoInteractions(assembly.mantle);
|
||||
assertTrue(assembly.ownsMantle);
|
||||
assertFalse(assembly.hash32.isCancelled());
|
||||
}
|
||||
}
|
||||
@@ -43,8 +43,8 @@ public class EngineShutdownOwnershipContractTest {
|
||||
String cleanup = source.substring(cleanupStart, cleanupEnd);
|
||||
int ownershipClose = cleanup.indexOf("NativeStructureOwnershipStore.close(engine)");
|
||||
int ownershipGate = cleanup.indexOf("if (ownershipFailure == null)", ownershipClose);
|
||||
int detachedRelease = cleanup.indexOf("closeDetachedGenerationRuntimes(cleanupFailure)", ownershipGate);
|
||||
int runtimeRelease = cleanup.indexOf("closeRuntime(engine.runtime, cleanupFailure)", ownershipGate);
|
||||
int detachedRelease = cleanup.indexOf("closeDetachedGenerationRuntimes(null)", ownershipGate);
|
||||
int runtimeRelease = cleanup.indexOf("closeRuntime(engine.runtime, null)", ownershipGate);
|
||||
int closedPublication = cleanup.indexOf("engine.closed = true", ownershipGate);
|
||||
|
||||
assertTrue(ownershipClose >= 0);
|
||||
|
||||
Reference in New Issue
Block a user