mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-10-26 14:00:26 +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:
1 parent
80155cef93
commit
c27a233a50
4 files changed
+132
-9
No files matched your search
@@ -192,8 +192,17 @@ final class EngineShutdownSequence {
|
|||||||
() -> NativeStructureOwnershipStore.close(engine));
|
() -> NativeStructureOwnershipStore.close(engine));
|
||||||
cleanupFailure = appendFailure(cleanupFailure, ownershipFailure);
|
cleanupFailure = appendFailure(cleanupFailure, ownershipFailure);
|
||||||
if (ownershipFailure == null) {
|
if (ownershipFailure == null) {
|
||||||
cleanupFailure = closeDetachedGenerationRuntimes(cleanupFailure);
|
Throwable runtimeFailure = closeDetachedGenerationRuntimes(null);
|
||||||
cleanupFailure = closeRuntime(engine.runtime, cleanupFailure);
|
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;
|
engine.runtime = null;
|
||||||
cleanupFailure = runCleanup(cleanupFailure, engine.publishedTarget::close);
|
cleanupFailure = runCleanup(cleanupFailure, engine.publishedTarget::close);
|
||||||
cleanupFailure = runCleanup(cleanupFailure, engine.engineDataStore::releaseEngineData);
|
cleanupFailure = runCleanup(cleanupFailure, engine.engineDataStore::releaseEngineData);
|
||||||
@@ -229,11 +238,15 @@ final class EngineShutdownSequence {
|
|||||||
assembly.mode.close();
|
assembly.mode.close();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
failure = runCleanup(failure, () -> {
|
Throwable complexFailure = runCleanup(null, () -> {
|
||||||
if (assembly.complex != null) {
|
if (assembly.complex != null) {
|
||||||
assembly.complex.close();
|
assembly.complex.close();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
failure = appendFailure(failure, complexFailure);
|
||||||
|
if (complexFailure != null) {
|
||||||
|
return failure;
|
||||||
|
}
|
||||||
failure = runCleanup(failure, () -> {
|
failure = runCleanup(failure, () -> {
|
||||||
if (assembly.hash32 != null) {
|
if (assembly.hash32 != null) {
|
||||||
assembly.hash32.cancel(true);
|
assembly.hash32.cancel(true);
|
||||||
@@ -277,7 +290,11 @@ final class EngineShutdownSequence {
|
|||||||
return failure;
|
return failure;
|
||||||
}
|
}
|
||||||
failure = runCleanup(failure, generationRuntime.mode()::close);
|
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));
|
failure = runCleanup(failure, () -> generationRuntime.hash32().cancel(true));
|
||||||
if (generationRuntime.mantle() != retainedMantle) {
|
if (generationRuntime.mantle() != retainedMantle) {
|
||||||
failure = runCleanup(failure, generationRuntime.mantle()::saveAllNow);
|
failure = runCleanup(failure, generationRuntime.mantle()::saveAllNow);
|
||||||
@@ -290,10 +307,14 @@ final class EngineShutdownSequence {
|
|||||||
IrisEngine.GenerationRuntimeBinding binding = new IrisEngine.GenerationRuntimeBinding(
|
IrisEngine.GenerationRuntimeBinding binding = new IrisEngine.GenerationRuntimeBinding(
|
||||||
engine,
|
engine,
|
||||||
generationRuntime);
|
generationRuntime);
|
||||||
|
Throwable runtimeFailure;
|
||||||
try (IrisEngine.GenerationRuntimeScope ignored = engine.generationRuntimeScopes.open(binding)) {
|
try (IrisEngine.GenerationRuntimeScope ignored = engine.generationRuntimeScopes.open(binding)) {
|
||||||
failure = closeGenerationRuntime(generationRuntime, failure);
|
runtimeFailure = closeGenerationRuntime(generationRuntime, null);
|
||||||
} catch (Throwable scopeFailure) {
|
} catch (Throwable scopeFailure) {
|
||||||
failure = appendFailure(failure, scopeFailure);
|
runtimeFailure = scopeFailure;
|
||||||
|
}
|
||||||
|
if (runtimeFailure != null) {
|
||||||
|
return appendFailure(failure, runtimeFailure);
|
||||||
}
|
}
|
||||||
return closeDetachedTarget(generationRuntime.target(), failure);
|
return closeDetachedTarget(generationRuntime.target(), failure);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1783,9 +1783,9 @@ public class IrisComplex implements DataProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void close() {
|
public void close() {
|
||||||
resolvedTerrain.clear();
|
|
||||||
if (hydrologyRuntime != null) {
|
if (hydrologyRuntime != null) {
|
||||||
hydrologyRuntime.close();
|
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);
|
String cleanup = source.substring(cleanupStart, cleanupEnd);
|
||||||
int ownershipClose = cleanup.indexOf("NativeStructureOwnershipStore.close(engine)");
|
int ownershipClose = cleanup.indexOf("NativeStructureOwnershipStore.close(engine)");
|
||||||
int ownershipGate = cleanup.indexOf("if (ownershipFailure == null)", ownershipClose);
|
int ownershipGate = cleanup.indexOf("if (ownershipFailure == null)", ownershipClose);
|
||||||
int detachedRelease = cleanup.indexOf("closeDetachedGenerationRuntimes(cleanupFailure)", ownershipGate);
|
int detachedRelease = cleanup.indexOf("closeDetachedGenerationRuntimes(null)", ownershipGate);
|
||||||
int runtimeRelease = cleanup.indexOf("closeRuntime(engine.runtime, cleanupFailure)", ownershipGate);
|
int runtimeRelease = cleanup.indexOf("closeRuntime(engine.runtime, null)", ownershipGate);
|
||||||
int closedPublication = cleanup.indexOf("engine.closed = true", ownershipGate);
|
int closedPublication = cleanup.indexOf("engine.closed = true", ownershipGate);
|
||||||
|
|
||||||
assertTrue(ownershipClose >= 0);
|
assertTrue(ownershipClose >= 0);
|
||||||
|
|||||||
Reference in new issue
Block a user