mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-11-01 08:50:06 +00:00
Shared concurrent raster builds
- Made concurrent demands for one chunk wait for a single water raster build - Added a test that a second demand reuses the build already running
This commit is contained in:
1 parent
1899de7f3d
commit
0d148c69b5
2 files changed
+67
-1
No files matched your search
+39
-1
@@ -72,6 +72,7 @@ public final class HydrologyRuntime {
|
|||||||
private final RasterLru rasters;
|
private final RasterLru rasters;
|
||||||
private final ThreadLocal<LastRaster> last;
|
private final ThreadLocal<LastRaster> last;
|
||||||
private final Set<Long> queued;
|
private final Set<Long> queued;
|
||||||
|
private final ConcurrentHashMap<Long, CompletableFuture<ChunkWaterRaster>> building;
|
||||||
private final RasterFailures failures;
|
private final RasterFailures failures;
|
||||||
private final RasterFailures windowPreviewFailures;
|
private final RasterFailures windowPreviewFailures;
|
||||||
private final RasterFailures regionPreviewFailures;
|
private final RasterFailures regionPreviewFailures;
|
||||||
@@ -94,6 +95,7 @@ public final class HydrologyRuntime {
|
|||||||
this.rasters = null;
|
this.rasters = null;
|
||||||
this.last = null;
|
this.last = null;
|
||||||
this.queued = null;
|
this.queued = null;
|
||||||
|
this.building = null;
|
||||||
this.failures = null;
|
this.failures = null;
|
||||||
this.windowPreviewFailures = null;
|
this.windowPreviewFailures = null;
|
||||||
this.regionPreviewFailures = null;
|
this.regionPreviewFailures = null;
|
||||||
@@ -120,6 +122,7 @@ public final class HydrologyRuntime {
|
|||||||
this.rasters = new RasterLru();
|
this.rasters = new RasterLru();
|
||||||
this.last = ThreadLocal.withInitial(LastRaster::new);
|
this.last = ThreadLocal.withInitial(LastRaster::new);
|
||||||
this.queued = ConcurrentHashMap.newKeySet();
|
this.queued = ConcurrentHashMap.newKeySet();
|
||||||
|
this.building = new ConcurrentHashMap<>();
|
||||||
this.failures = new RasterFailures(ticker);
|
this.failures = new RasterFailures(ticker);
|
||||||
this.windowPreviewFailures = new RasterFailures(ticker);
|
this.windowPreviewFailures = new RasterFailures(ticker);
|
||||||
this.regionPreviewFailures = new RasterFailures(ticker);
|
this.regionPreviewFailures = new RasterFailures(ticker);
|
||||||
@@ -234,7 +237,7 @@ public final class HydrologyRuntime {
|
|||||||
requireOpen();
|
requireOpen();
|
||||||
long key = key(chunkX, chunkZ);
|
long key = key(chunkX, chunkZ);
|
||||||
ChunkWaterRaster known = remembered(key, slot);
|
ChunkWaterRaster known = remembered(key, slot);
|
||||||
return known != null ? known : remember(key, planned(chunkX, chunkZ, key));
|
return known != null ? known : build(chunkX, chunkZ, key, slot);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ChunkWaterRaster rasterSnapshot(int chunkX, int chunkZ) {
|
public ChunkWaterRaster rasterSnapshot(int chunkX, int chunkZ) {
|
||||||
@@ -501,6 +504,41 @@ public final class HydrologyRuntime {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ChunkWaterRaster build(int chunkX, int chunkZ, long key, LastRaster slot) {
|
||||||
|
CompletableFuture<ChunkWaterRaster> claim = new CompletableFuture<>();
|
||||||
|
CompletableFuture<ChunkWaterRaster> running = building.putIfAbsent(key, claim);
|
||||||
|
if (running != null) {
|
||||||
|
ChunkWaterRaster built = built(running);
|
||||||
|
slot.remember(key, built);
|
||||||
|
return built;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
ChunkWaterRaster known = remembered(key, slot);
|
||||||
|
ChunkWaterRaster raster = known != null ? known : remember(key, planned(chunkX, chunkZ, key));
|
||||||
|
claim.complete(raster);
|
||||||
|
return raster;
|
||||||
|
} catch (RuntimeException | Error failure) {
|
||||||
|
claim.completeExceptionally(failure);
|
||||||
|
throw failure;
|
||||||
|
} finally {
|
||||||
|
building.remove(key, claim);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ChunkWaterRaster built(CompletableFuture<ChunkWaterRaster> running) {
|
||||||
|
try {
|
||||||
|
return running.join();
|
||||||
|
} catch (CompletionException failure) {
|
||||||
|
if (failure.getCause() instanceof RuntimeException cause) {
|
||||||
|
throw cause;
|
||||||
|
}
|
||||||
|
if (failure.getCause() instanceof Error cause) {
|
||||||
|
throw cause;
|
||||||
|
}
|
||||||
|
throw failure;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private ChunkWaterRaster planned(int chunkX, int chunkZ, long chunk) {
|
private ChunkWaterRaster planned(int chunkX, int chunkZ, long chunk) {
|
||||||
try {
|
try {
|
||||||
List<WindowKey> windowKeys = neighbourhood(chunkX, chunkZ);
|
List<WindowKey> windowKeys = neighbourhood(chunkX, chunkZ);
|
||||||
|
|||||||
+28
@@ -215,6 +215,34 @@ public class HydrologyRuntimeTest {
|
|||||||
assertEquals(planned + 1L, runtime.previewRevision());
|
assertEquals(planned + 1L, runtime.previewRevision());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test(timeout = 180_000L)
|
||||||
|
public void concurrentDemandsShareOneRasterBuild() throws Exception {
|
||||||
|
FakePlanners planners = new FakePlanners(settings(0, false));
|
||||||
|
HydrologyRuntime runtime = runtime(planners, () -> false);
|
||||||
|
runtime.prepareWindowsAround(0, 0, 0, 0, 1).get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
|
||||||
|
CountDownLatch gate = new CountDownLatch(1);
|
||||||
|
planners.naturalGate = gate;
|
||||||
|
AtomicReference<ChunkWaterRaster> first = new AtomicReference<>();
|
||||||
|
AtomicReference<ChunkWaterRaster> second = new AtomicReference<>();
|
||||||
|
Thread leader = new Thread(() -> first.set(runtime.raster(0, 0)));
|
||||||
|
Thread follower = new Thread(() -> second.set(runtime.raster(0, 0)));
|
||||||
|
try {
|
||||||
|
leader.start();
|
||||||
|
await(() -> planners.naturalSamples.get() > 0, "the first demand starts sampling");
|
||||||
|
follower.start();
|
||||||
|
await(() -> follower.getState() == Thread.State.WAITING || follower.getState() == Thread.State.TIMED_WAITING,
|
||||||
|
"the second demand waits");
|
||||||
|
assertEquals(0, planners.naturalSamplesOn(follower));
|
||||||
|
} finally {
|
||||||
|
gate.countDown();
|
||||||
|
leader.join(TimeUnit.SECONDS.toMillis(TIMEOUT_SECONDS));
|
||||||
|
follower.join(TimeUnit.SECONDS.toMillis(TIMEOUT_SECONDS));
|
||||||
|
}
|
||||||
|
assertFalse(first.get().provisional());
|
||||||
|
assertSame(first.get(), second.get());
|
||||||
|
assertEquals(0, planners.naturalSamplesOn(follower));
|
||||||
|
}
|
||||||
|
|
||||||
@Test(timeout = 180_000L)
|
@Test(timeout = 180_000L)
|
||||||
public void failedRasterDoesNotAdvancePreviewRevision() throws Exception {
|
public void failedRasterDoesNotAdvancePreviewRevision() throws Exception {
|
||||||
FakePlanners planners = new FakePlanners(settings(0, false));
|
FakePlanners planners = new FakePlanners(settings(0, false));
|
||||||
|
|||||||
Reference in new issue
Block a user