use completable futures
This commit is contained in:
parent
eaf0be6ef2
commit
5b14cad343
@ -8,7 +8,7 @@ import com.terraforged.core.region.chunk.ChunkGenTask;
|
||||
import com.terraforged.core.region.chunk.ChunkReader;
|
||||
import com.terraforged.core.region.chunk.ChunkWriter;
|
||||
import com.terraforged.core.region.chunk.ChunkZoomTask;
|
||||
import com.terraforged.core.util.concurrent.ThreadPool;
|
||||
import com.terraforged.core.util.concurrent.batcher.Batcher;
|
||||
import com.terraforged.core.world.heightmap.Heightmap;
|
||||
import com.terraforged.core.world.terrain.Terrain;
|
||||
|
||||
@ -106,7 +106,7 @@ public class Region implements Extent {
|
||||
}
|
||||
}
|
||||
|
||||
public void generate(Heightmap heightmap, ThreadPool.Batcher batcher) {
|
||||
public void generate(Heightmap heightmap, Batcher batcher) {
|
||||
for (int cz = 0; cz < chunkSize.total; cz++) {
|
||||
for (int cx = 0; cx < chunkSize.total; cx++) {
|
||||
int index = chunkSize.indexOf(cx, cz);
|
||||
@ -117,7 +117,7 @@ public class Region implements Extent {
|
||||
}
|
||||
}
|
||||
|
||||
public void generateZoom(Heightmap heightmap, float offsetX, float offsetZ, float zoom, ThreadPool.Batcher batcher) {
|
||||
public void generateZoom(Heightmap heightmap, float offsetX, float offsetZ, float zoom, Batcher batcher) {
|
||||
float translateX = offsetX - ((blockSize.total * zoom) / 2F);
|
||||
float translateZ = offsetZ - ((blockSize.total * zoom) / 2F);
|
||||
for (int cz = 0; cz < chunkSize.total; cz++) {
|
||||
|
@ -2,26 +2,24 @@ package com.terraforged.core.region;
|
||||
|
||||
import com.terraforged.core.region.chunk.ChunkReader;
|
||||
import com.terraforged.core.util.Cache;
|
||||
import com.terraforged.core.util.FutureValue;
|
||||
import com.terraforged.core.world.heightmap.RegionExtent;
|
||||
import me.dags.noise.util.NoiseUtil;
|
||||
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class RegionCache implements RegionExtent {
|
||||
|
||||
private final boolean queuing;
|
||||
private final RegionGenerator renderer;
|
||||
private final Cache<Long, Future<Region>> cache;
|
||||
private final Cache<Long, CompletableFuture<Region>> cache;
|
||||
|
||||
private Region cachedRegion = null;
|
||||
|
||||
public RegionCache(boolean queueNeighbours, RegionGenerator renderer) {
|
||||
this.renderer = renderer;
|
||||
this.queuing = queueNeighbours;
|
||||
this.cache = new com.terraforged.core.util.Cache<>(180, 60, TimeUnit.SECONDS);
|
||||
this.cache = new Cache<>(180, 60, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -30,9 +28,9 @@ public class RegionCache implements RegionExtent {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Region> getRegionAsync(int regionX, int regionZ) {
|
||||
public CompletableFuture<Region> getRegionAsync(int regionX, int regionZ) {
|
||||
long id = NoiseUtil.seed(regionX, regionZ);
|
||||
Future<Region> future = cache.get(id);
|
||||
CompletableFuture<Region> future = cache.get(id);
|
||||
if (future == null) {
|
||||
future = renderer.getRegionAsync(regionX, regionZ);
|
||||
cache.put(id, future);
|
||||
@ -55,17 +53,13 @@ public class RegionCache implements RegionExtent {
|
||||
}
|
||||
|
||||
long id = NoiseUtil.seed(regionX, regionZ);
|
||||
Future<Region> futureRegion = cache.get(id);
|
||||
CompletableFuture<Region> futureRegion = cache.get(id);
|
||||
|
||||
if (futureRegion == null) {
|
||||
cachedRegion = renderer.generateRegion(regionX, regionZ);
|
||||
cache.put(id, new FutureValue<>(cachedRegion));
|
||||
cache.put(id, CompletableFuture.completedFuture(cachedRegion));
|
||||
} else {
|
||||
try {
|
||||
cachedRegion = futureRegion.get();
|
||||
} catch (InterruptedException | ExecutionException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
cachedRegion = futureRegion.join();
|
||||
}
|
||||
|
||||
if (queuing) {
|
||||
|
@ -2,12 +2,12 @@ package com.terraforged.core.region;
|
||||
|
||||
import com.terraforged.core.util.concurrent.ObjectPool;
|
||||
import com.terraforged.core.util.concurrent.ThreadPool;
|
||||
import com.terraforged.core.util.concurrent.batcher.Batcher;
|
||||
import com.terraforged.core.world.WorldGenerator;
|
||||
import com.terraforged.core.world.WorldGeneratorFactory;
|
||||
import com.terraforged.core.world.heightmap.RegionExtent;
|
||||
|
||||
import java.util.concurrent.ForkJoinPool;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class RegionGenerator implements RegionExtent {
|
||||
|
||||
@ -42,23 +42,23 @@ public class RegionGenerator implements RegionExtent {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Region> getRegionAsync(int regionX, int regionZ) {
|
||||
public CompletableFuture<Region> getRegionAsync(int regionX, int regionZ) {
|
||||
return generate(regionX, regionZ);
|
||||
}
|
||||
|
||||
public Future<Region> generate(int regionX, int regionZ) {
|
||||
return ForkJoinPool.commonPool().submit(() -> generateRegion(regionX, regionZ));
|
||||
public CompletableFuture<Region> generate(int regionX, int regionZ) {
|
||||
return CompletableFuture.supplyAsync(() -> generateRegion(regionX, regionZ));
|
||||
}
|
||||
|
||||
public Future<Region> generate(float centerX, float centerZ, float zoom, boolean filter) {
|
||||
return ForkJoinPool.commonPool().submit(() -> generateRegion(centerX, centerZ, zoom, filter));
|
||||
public CompletableFuture<Region> generate(float centerX, float centerZ, float zoom, boolean filter) {
|
||||
return CompletableFuture.supplyAsync(() -> generateRegion(centerX, centerZ, zoom, filter));
|
||||
}
|
||||
|
||||
public Region generateRegion(int regionX, int regionZ) {
|
||||
try (ObjectPool.Item<WorldGenerator> item = genPool.get()) {
|
||||
WorldGenerator generator = item.getValue();
|
||||
Region region = new Region(regionX, regionZ, factor, border);
|
||||
try (ThreadPool.Batcher batcher = threadPool.batcher(region.getChunkCount())) {
|
||||
try (Batcher batcher = threadPool.batcher(region.getChunkCount())) {
|
||||
region.generate(generator.getHeightmap(), batcher);
|
||||
}
|
||||
postProcess(region, generator);
|
||||
@ -75,7 +75,7 @@ public class RegionGenerator implements RegionExtent {
|
||||
try (ObjectPool.Item<WorldGenerator> item = genPool.get()) {
|
||||
WorldGenerator generator = item.getValue();
|
||||
Region region = new Region(0, 0, factor, border);
|
||||
try (ThreadPool.Batcher batcher = threadPool.batcher(region.getChunkCount())) {
|
||||
try (Batcher batcher = threadPool.batcher(region.getChunkCount())) {
|
||||
region.generateZoom(generator.getHeightmap(), centerX, centerZ, zoom, batcher);
|
||||
}
|
||||
region.check();
|
||||
|
@ -1,7 +1,9 @@
|
||||
package com.terraforged.core.util.concurrent;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import com.terraforged.core.util.concurrent.batcher.AsyncBatcher;
|
||||
import com.terraforged.core.util.concurrent.batcher.Batcher;
|
||||
import com.terraforged.core.util.concurrent.batcher.SyncBatcher;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
@ -10,27 +12,27 @@ import java.util.concurrent.Future;
|
||||
|
||||
public class ThreadPool {
|
||||
|
||||
public static final int DEFAULT_POOL_SIZE = Math.max(2, (Runtime.getRuntime().availableProcessors() / 2) + 1);
|
||||
public static final int DEFAULT_POOL_SIZE = defaultPoolSize();
|
||||
|
||||
private static final Object lock = new Object();
|
||||
|
||||
private static ThreadPool instance = new ThreadPool();
|
||||
|
||||
private final int size;
|
||||
private final int poolSize;
|
||||
private final ExecutorService service;
|
||||
|
||||
private ThreadPool() {
|
||||
this.service = ForkJoinPool.commonPool();
|
||||
this.size = -1;
|
||||
this.poolSize = -1;
|
||||
}
|
||||
|
||||
public ThreadPool(int size) {
|
||||
this.service = Executors.newFixedThreadPool(size);
|
||||
this.size = size;
|
||||
this.poolSize = size;
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
if (size > 0) {
|
||||
if (poolSize > 0) {
|
||||
service.shutdown();
|
||||
}
|
||||
}
|
||||
@ -44,47 +46,15 @@ public class ThreadPool {
|
||||
}
|
||||
|
||||
public Batcher batcher(int size) {
|
||||
return new Batcher(size);
|
||||
}
|
||||
|
||||
public class Batcher implements AutoCloseable {
|
||||
|
||||
private final List<Future<?>> tasks;
|
||||
|
||||
public Batcher(int size) {
|
||||
tasks = new ArrayList<>(size);
|
||||
}
|
||||
|
||||
public void submit(Runnable task) {
|
||||
tasks.add(ThreadPool.this.submit(task));
|
||||
}
|
||||
|
||||
public void submit(Callable<?> task) {
|
||||
tasks.add(ThreadPool.this.submit(task));
|
||||
}
|
||||
|
||||
public void await() {
|
||||
boolean hasMore = true;
|
||||
while (hasMore) {
|
||||
hasMore = false;
|
||||
for (Future<?> future : tasks) {
|
||||
if (!future.isDone()) {
|
||||
hasMore = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
tasks.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
await();
|
||||
if (this.poolSize != -1 && this.poolSize < 3) {
|
||||
return new SyncBatcher();
|
||||
}
|
||||
return new AsyncBatcher(service, size);
|
||||
}
|
||||
|
||||
public static ThreadPool getFixed(int size) {
|
||||
synchronized (lock) {
|
||||
if (instance.size != size) {
|
||||
if (instance.poolSize != size) {
|
||||
instance.shutdown();
|
||||
instance = new ThreadPool(size);
|
||||
}
|
||||
@ -94,7 +64,7 @@ public class ThreadPool {
|
||||
|
||||
public static ThreadPool getFixed() {
|
||||
synchronized (lock) {
|
||||
if (instance.size == -1) {
|
||||
if (instance.poolSize == -1) {
|
||||
instance = new ThreadPool(ThreadPool.DEFAULT_POOL_SIZE);
|
||||
}
|
||||
return instance;
|
||||
@ -103,7 +73,7 @@ public class ThreadPool {
|
||||
|
||||
public static ThreadPool getCommon() {
|
||||
synchronized (lock) {
|
||||
if (instance.size != -1) {
|
||||
if (instance.poolSize != -1) {
|
||||
instance.shutdown();
|
||||
instance = new ThreadPool();
|
||||
}
|
||||
@ -118,4 +88,9 @@ public class ThreadPool {
|
||||
instance = new ThreadPool();
|
||||
}
|
||||
}
|
||||
|
||||
private static int defaultPoolSize() {
|
||||
int threads = Runtime.getRuntime().availableProcessors();
|
||||
return Math.max(1, (threads / 3) * 2);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,42 @@
|
||||
package com.terraforged.core.util.concurrent.batcher;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
public class AsyncBatcher implements Batcher {
|
||||
|
||||
private final List<Future<?>> tasks;
|
||||
private final ExecutorService executor;
|
||||
|
||||
public AsyncBatcher(ExecutorService executor, int size) {
|
||||
this.executor = executor;
|
||||
this.tasks = new ArrayList<>(size);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void submit(Runnable task) {
|
||||
tasks.add(executor.submit(task));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void submit(Callable<?> task) {
|
||||
tasks.add(executor.submit(task));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
boolean hasMore = true;
|
||||
while (hasMore) {
|
||||
hasMore = false;
|
||||
for (Future<?> future : tasks) {
|
||||
if (!future.isDone()) {
|
||||
hasMore = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
tasks.clear();
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package com.terraforged.core.util.concurrent.batcher;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
public interface Batcher extends AutoCloseable {
|
||||
|
||||
void submit(Runnable task);
|
||||
|
||||
void submit(Callable<?> task);
|
||||
|
||||
@Override
|
||||
void close();
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
package com.terraforged.core.util.concurrent.batcher;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
public class SyncBatcher implements Batcher {
|
||||
|
||||
@Override
|
||||
public void submit(Runnable task) {
|
||||
task.run();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void submit(Callable<?> task) {
|
||||
try {
|
||||
task.call();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
|
||||
}
|
||||
}
|
@ -9,8 +9,7 @@ import com.terraforged.core.world.terrain.Terrain;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public interface RegionExtent extends Extent {
|
||||
|
||||
@ -18,7 +17,7 @@ public interface RegionExtent extends Extent {
|
||||
|
||||
Region getRegion(int regionX, int regionZ);
|
||||
|
||||
Future<Region> getRegionAsync(int regionX, int regionZ);
|
||||
CompletableFuture<Region> getRegionAsync(int regionX, int regionZ);
|
||||
|
||||
default ChunkReader getChunk(int chunkX, int chunkZ) {
|
||||
int regionX = chunkToRegion(chunkX);
|
||||
@ -27,8 +26,8 @@ public interface RegionExtent extends Extent {
|
||||
return region.getChunk(chunkX, chunkZ);
|
||||
}
|
||||
|
||||
default List<Future<Region>> getRegions(int minRegionX, int minRegionZ, int maxRegionX, int maxRegionZ) {
|
||||
List<Future<Region>> regions = new LinkedList<>();
|
||||
default List<CompletableFuture<Region>> getRegions(int minRegionX, int minRegionZ, int maxRegionX, int maxRegionZ) {
|
||||
List<CompletableFuture<Region>> regions = new LinkedList<>();
|
||||
for (int rz = minRegionZ; rz <= maxRegionZ; rz++) {
|
||||
for (int rx = minRegionX; rx <= maxRegionX; rx++) {
|
||||
regions.add(getRegionAsync(rx, rz));
|
||||
@ -43,18 +42,14 @@ public interface RegionExtent extends Extent {
|
||||
int minRegionZ = chunkToRegion(Size.blockToChunk(minZ));
|
||||
int maxRegionX = chunkToRegion(Size.blockToChunk(maxX));
|
||||
int maxRegionZ = chunkToRegion(Size.blockToChunk(maxZ));
|
||||
List<Future<Region>> regions = getRegions(minRegionX, minRegionZ, maxRegionX, maxRegionZ);
|
||||
List<CompletableFuture<Region>> regions = getRegions(minRegionX, minRegionZ, maxRegionX, maxRegionZ);
|
||||
while (!regions.isEmpty()) {
|
||||
regions.removeIf(future -> {
|
||||
if (!future.isDone()) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
Region region = future.get();
|
||||
Region region = future.join();
|
||||
region.visit(minX, minZ, maxX, maxZ, visitor);
|
||||
} catch (InterruptedException | ExecutionException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user