mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-10-26 05:50:39 +00:00
One thread frame per engine
- runtime assembly, biome environment and runtime scope share one ThreadLocal frame - engine getters do a single ThreadLocal lookup
This commit is contained in:
1 parent
badca54e5d
commit
d0008086ac
11 files changed
+296
-151
No files matched your search
@@ -112,7 +112,7 @@ public class EngineShutdownDrainTest {
|
||||
doAnswer(invocation -> {
|
||||
Future<?> continuation = worker.submit(() -> {
|
||||
try (IrisEngine.GenerationRuntimeScope ignored = fixture.engine.openGenerationRuntimeScope(binding)) {
|
||||
assertSame(binding, fixture.engine.generationRuntimeScopes.current());
|
||||
assertSame(binding, fixture.engine.threadState.binding());
|
||||
assertSame(fixture.runtime, fixture.engine.runtime);
|
||||
assertTrue(fixture.engine.getClosing().get());
|
||||
assertThrows(IllegalStateException.class, () -> fixture.engine.requireRunning("start generation"));
|
||||
@@ -513,16 +513,16 @@ public class EngineShutdownDrainTest {
|
||||
assembly.mantle = mock(EngineMantle.class);
|
||||
assembly.ownsMantle = true;
|
||||
RuntimeAssembly previous = mock(RuntimeAssembly.class);
|
||||
fixture.engine.runtimeAssembly.set(previous);
|
||||
fixture.engine.threadState.setAssembly(previous);
|
||||
IllegalStateException failure = new IllegalStateException("Planner still active");
|
||||
doAnswer(invocation -> {
|
||||
assertSame(assembly, fixture.engine.runtimeAssembly.get());
|
||||
assertSame(assembly, fixture.engine.threadState.assembly());
|
||||
throw failure;
|
||||
}).when(assembly.complex).close();
|
||||
|
||||
assertSame(failure, fixture.shutdown.closeAssembly(assembly, null));
|
||||
assertSame(previous, fixture.engine.runtimeAssembly.get());
|
||||
fixture.engine.runtimeAssembly.remove();
|
||||
assertSame(previous, fixture.engine.threadState.assembly());
|
||||
fixture.engine.threadState.setAssembly(null);
|
||||
assertTrue(fixture.shutdown.retainsData(detachedTarget.getData()));
|
||||
assertTrue(fixture.shutdown.closeDetachedTarget(detachedTarget, null) instanceof IllegalStateException);
|
||||
verify(detachedTarget, never()).close();
|
||||
@@ -531,13 +531,13 @@ public class EngineShutdownDrainTest {
|
||||
verify(fixture.target, never()).close();
|
||||
verifyNoInteractions(assembly.mantle);
|
||||
doAnswer(invocation -> {
|
||||
assertSame(assembly, fixture.engine.runtimeAssembly.get());
|
||||
assertSame(assembly, fixture.engine.threadState.assembly());
|
||||
return null;
|
||||
}).when(assembly.complex).close();
|
||||
fixture.shutdown.close();
|
||||
}
|
||||
|
||||
assertNull(fixture.engine.runtimeAssembly.get());
|
||||
assertNull(fixture.engine.threadState.assembly());
|
||||
assertFalse(assembly.ownsMantle);
|
||||
assertFalse(fixture.shutdown.retainsData(detachedTarget.getData()));
|
||||
verify(assembly.mantle).close();
|
||||
@@ -851,10 +851,9 @@ public class EngineShutdownDrainTest {
|
||||
setField(engine, "shutdownSequence", shutdown);
|
||||
setField(engine, "engineDataStore", mock(EngineDataStore.class));
|
||||
setField(engine, "lifecycleLock", new Object());
|
||||
setField(engine, "runtimeAssembly", new ThreadLocal<RuntimeAssembly>());
|
||||
setField(engine, "threadState", new EngineThreadState());
|
||||
setField(engine, "detachedGenerationRuntimes", ConcurrentHashMap.newKeySet());
|
||||
setField(engine, "retiringGenerationRuntimes", ConcurrentHashMap.newKeySet());
|
||||
setField(engine, "generationRuntimeScopes", new GenerationRuntimeScopeState());
|
||||
AtomicBoolean closing = new AtomicBoolean();
|
||||
setField(engine, "closing", closing);
|
||||
when(engine.getClosing()).thenReturn(closing);
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
package art.arcane.iris.generation.runtime;
|
||||
|
||||
import art.arcane.iris.generation.runtime.EngineRuntimeBuilder.RuntimeAssembly;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertThrows;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
public class EngineThreadStateTest {
|
||||
@Test
|
||||
public void bindingsShareOneFrameAndReleaseItWhenAllAreCleared() {
|
||||
EngineThreadState state = new EngineThreadState();
|
||||
RuntimeAssembly assembly = mock(RuntimeAssembly.class);
|
||||
IrisEngine.GenerationRuntimeBinding binding = new IrisEngine.GenerationRuntimeBinding(
|
||||
mock(IrisEngine.class), mock(GenerationRuntime.class));
|
||||
|
||||
assertNull(state.current());
|
||||
state.setAssembly(assembly);
|
||||
IrisEngine.GenerationRuntimeScope scope = state.open(binding);
|
||||
EngineThreadState.Frame frame = state.current();
|
||||
assertSame(assembly, frame.assembly);
|
||||
assertSame(binding, frame.binding);
|
||||
|
||||
state.setAssembly(null);
|
||||
assertSame(frame, state.current());
|
||||
assertSame(binding, state.binding());
|
||||
scope.close();
|
||||
assertNull(state.current());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nestedScopesRestoreThePreviousBindingInOrder() {
|
||||
EngineThreadState state = new EngineThreadState();
|
||||
IrisEngine.GenerationRuntimeBinding outer = new IrisEngine.GenerationRuntimeBinding(
|
||||
mock(IrisEngine.class), mock(GenerationRuntime.class));
|
||||
IrisEngine.GenerationRuntimeBinding inner = new IrisEngine.GenerationRuntimeBinding(
|
||||
mock(IrisEngine.class), mock(GenerationRuntime.class));
|
||||
|
||||
IrisEngine.GenerationRuntimeScope outerScope = state.open(outer);
|
||||
IrisEngine.GenerationRuntimeScope innerScope = state.open(inner);
|
||||
assertThrows(IllegalStateException.class, outerScope::close);
|
||||
innerScope.close();
|
||||
assertSame(outer, state.binding());
|
||||
outerScope.close();
|
||||
assertNull(state.current());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void framesAreThreadConfinedAndScopesRejectForeignClose() throws InterruptedException {
|
||||
EngineThreadState state = new EngineThreadState();
|
||||
IrisEngine.GenerationRuntimeBinding binding = new IrisEngine.GenerationRuntimeBinding(
|
||||
mock(IrisEngine.class), mock(GenerationRuntime.class));
|
||||
IrisEngine.GenerationRuntimeScope scope = state.open(binding);
|
||||
AtomicReference<Object> seen = new AtomicReference<>(binding);
|
||||
AtomicReference<Throwable> foreignClose = new AtomicReference<>();
|
||||
Thread other = new Thread(() -> {
|
||||
seen.set(state.current());
|
||||
try {
|
||||
scope.close();
|
||||
} catch (Throwable failure) {
|
||||
foreignClose.set(failure);
|
||||
}
|
||||
});
|
||||
other.start();
|
||||
other.join();
|
||||
|
||||
assertNull(seen.get());
|
||||
assertSame(IllegalStateException.class, foreignClose.get().getClass());
|
||||
scope.close();
|
||||
assertNull(state.current());
|
||||
}
|
||||
}
|
||||
+4
-6
@@ -103,7 +103,7 @@ public class IrisCarveWallPaletteScopeTest {
|
||||
IrisContext.Scope context = IrisContext.open(fixture.engine, 71L, fixture.context);
|
||||
MockedStatic<TerrainMatterView> terrain = mockStatic(TerrainMatterView.class)) {
|
||||
if (mode == Mode.ASSEMBLY) {
|
||||
fixture.engine.runtimeAssembly.set(fixture.assembly);
|
||||
fixture.engine.threadState.setAssembly(fixture.assembly);
|
||||
}
|
||||
terrain.when(() -> TerrainMatterView.iterate(eq(fixture.chunk), eq(MatterCavern.class), any()))
|
||||
.thenAnswer(call -> {
|
||||
@@ -137,7 +137,7 @@ public class IrisCarveWallPaletteScopeTest {
|
||||
verify(fixture.chunk).release();
|
||||
assertEquals(1, fixture.dataLookups.get());
|
||||
} finally {
|
||||
fixture.engine.runtimeAssembly.remove();
|
||||
fixture.engine.threadState.setAssembly(null);
|
||||
}
|
||||
}
|
||||
assertSame(fixture.active.data(), fixture.engine.getData());
|
||||
@@ -199,9 +199,7 @@ public class IrisCarveWallPaletteScopeTest {
|
||||
active = runtime(mock(IrisData.class), mock(IrisComplex.class), engineMantle);
|
||||
detached = runtime(mock(IrisData.class), mock(IrisComplex.class), engineMantle);
|
||||
field(engine, IrisEngine.class, "lifecycleLock", new Object());
|
||||
field(engine, IrisEngine.class, "runtimeAssembly", new ThreadLocal<EngineRuntimeBuilder.RuntimeAssembly>());
|
||||
field(engine, IrisEngine.class, "biomeEnvironmentScopes", new ThreadLocal<>());
|
||||
field(engine, IrisEngine.class, "generationRuntimeScopes", new GenerationRuntimeScopeState());
|
||||
field(engine, IrisEngine.class, "threadState", new EngineThreadState());
|
||||
field(engine, IrisEngine.class, "detachedGenerationRuntimes", Collections.newSetFromMap(new IdentityHashMap<>()));
|
||||
field(engine, IrisEngine.class, "retiringGenerationRuntimes", Collections.newSetFromMap(new IdentityHashMap<>()));
|
||||
engine.detachedGenerationRuntimes.add(detached);
|
||||
@@ -255,7 +253,7 @@ public class IrisCarveWallPaletteScopeTest {
|
||||
private IrisBiome scopedBiome(IrisBiome biome) {
|
||||
try (IrisEngine.GenerationRuntimeScope ignored = engine.openGenerationRuntimeScope(
|
||||
new IrisEngine.GenerationRuntimeBinding(engine, active))) {
|
||||
assertSame(engine.runtimeAssembly.get() == null ? active.data() : assemblyData, engine.getData());
|
||||
assertSame(engine.threadState.assembly() == null ? active.data() : assemblyData, engine.getData());
|
||||
}
|
||||
return biome;
|
||||
}
|
||||
|
||||
+4
-4
@@ -62,7 +62,7 @@ public class IrisEngineEntryHydrologyTest {
|
||||
doAnswer(invocation -> {
|
||||
assertSame(engine, IrisContext.require().getEngine());
|
||||
assertEquals(93L, IrisContext.require().getGenerationSessionId());
|
||||
assertSame(binding, engine.generationRuntimeScopes.current());
|
||||
assertSame(binding, engine.threadState.binding());
|
||||
return null;
|
||||
}).when(hydrology).prepareChunkColumns(0, 0);
|
||||
AtomicReference<Callable<Void>> queued = new AtomicReference<>();
|
||||
@@ -75,7 +75,7 @@ public class IrisEngineEntryHydrologyTest {
|
||||
verify(hydrology).prepareChunkColumns(0, 0);
|
||||
verify(lease).close();
|
||||
assertNull(IrisContext.get());
|
||||
assertNull(engine.generationRuntimeScopes.current());
|
||||
assertNull(engine.threadState.binding());
|
||||
tasks.drainBackgroundTasks("test").requireComplete("test");
|
||||
}
|
||||
}
|
||||
@@ -325,9 +325,9 @@ public class IrisEngineEntryHydrologyTest {
|
||||
Field closing = IrisEngine.class.getDeclaredField("closing");
|
||||
closing.setAccessible(true);
|
||||
closing.set(engine, new AtomicBoolean());
|
||||
Field scopes = IrisEngine.class.getDeclaredField("generationRuntimeScopes");
|
||||
Field scopes = IrisEngine.class.getDeclaredField("threadState");
|
||||
scopes.setAccessible(true);
|
||||
scopes.set(engine, new GenerationRuntimeScopeState());
|
||||
scopes.set(engine, new EngineThreadState());
|
||||
Field lifecycle = IrisEngine.class.getDeclaredField("lifecycleLock");
|
||||
lifecycle.setAccessible(true);
|
||||
lifecycle.set(engine, new Object());
|
||||
|
||||
+1
-3
@@ -923,9 +923,7 @@ public class IrisEngineGenerationRuntimeScopeTest {
|
||||
IrisEngine engine = mock(IrisEngine.class, CALLS_REAL_METHODS);
|
||||
setField(engine, "lifecycleLock", new Object());
|
||||
setField(engine, "generationHistoryRuntimeRouterLock", new Object());
|
||||
setField(engine, "runtimeAssembly", new ThreadLocal<EngineRuntimeBuilder.RuntimeAssembly>());
|
||||
setField(engine, "biomeEnvironmentScopes", new ThreadLocal<Object>());
|
||||
setField(engine, "generationRuntimeScopes", new GenerationRuntimeScopeState());
|
||||
setField(engine, "threadState", new EngineThreadState());
|
||||
Set<GenerationRuntime> detached = Collections.synchronizedSet(
|
||||
Collections.newSetFromMap(new IdentityHashMap<GenerationRuntime, Boolean>()));
|
||||
setField(engine, "detachedGenerationRuntimes", detached);
|
||||
|
||||
Reference in new issue
Block a user