2019-04-28 15:44:59 +00:00
|
|
|
package com.boydti.fawe.beta.implementation;
|
|
|
|
|
2019-05-02 14:45:03 +00:00
|
|
|
import com.boydti.fawe.Fawe;
|
2019-05-01 15:45:18 +00:00
|
|
|
import com.boydti.fawe.FaweCache;
|
2019-04-30 16:19:10 +00:00
|
|
|
import com.boydti.fawe.beta.IChunk;
|
2019-08-18 01:09:09 +00:00
|
|
|
import com.boydti.fawe.beta.IChunkGet;
|
|
|
|
import com.boydti.fawe.beta.IChunkSet;
|
2019-04-28 15:44:59 +00:00
|
|
|
import com.boydti.fawe.beta.IQueueExtent;
|
|
|
|
import com.boydti.fawe.beta.Trimable;
|
2019-04-30 16:19:10 +00:00
|
|
|
import com.boydti.fawe.config.Settings;
|
2019-04-28 15:44:59 +00:00
|
|
|
import com.boydti.fawe.object.collection.IterableThreadLocal;
|
2019-05-01 15:45:18 +00:00
|
|
|
import com.boydti.fawe.util.MemUtil;
|
2019-05-02 14:45:03 +00:00
|
|
|
import com.boydti.fawe.util.TaskManager;
|
2019-04-28 17:36:23 +00:00
|
|
|
import com.boydti.fawe.wrappers.WorldWrapper;
|
2019-07-22 06:02:51 +00:00
|
|
|
import com.google.common.util.concurrent.Futures;
|
2019-04-28 15:44:59 +00:00
|
|
|
import com.sk89q.worldedit.world.World;
|
|
|
|
import java.lang.ref.WeakReference;
|
|
|
|
import java.util.HashMap;
|
|
|
|
import java.util.Iterator;
|
|
|
|
import java.util.Map;
|
2019-05-02 08:27:33 +00:00
|
|
|
import java.util.concurrent.Callable;
|
2019-05-01 15:45:18 +00:00
|
|
|
import java.util.concurrent.ConcurrentLinkedQueue;
|
2019-07-18 16:07:31 +00:00
|
|
|
import java.util.concurrent.ExecutionException;
|
2019-04-30 16:19:10 +00:00
|
|
|
import java.util.concurrent.ForkJoinPool;
|
|
|
|
import java.util.concurrent.ForkJoinTask;
|
|
|
|
import java.util.concurrent.Future;
|
2019-05-02 08:27:33 +00:00
|
|
|
import java.util.concurrent.FutureTask;
|
2019-05-01 15:45:18 +00:00
|
|
|
import java.util.concurrent.ThreadPoolExecutor;
|
2019-07-22 06:02:51 +00:00
|
|
|
import java.util.function.Supplier;
|
2019-04-28 15:44:59 +00:00
|
|
|
|
2019-04-28 17:36:23 +00:00
|
|
|
/**
|
|
|
|
* Class which handles all the queues {@link IQueueExtent}
|
|
|
|
*/
|
2019-10-03 23:35:55 +00:00
|
|
|
@SuppressWarnings("UnstableApiUsage")
|
2019-05-02 14:45:03 +00:00
|
|
|
public abstract class QueueHandler implements Trimable, Runnable {
|
2019-08-06 15:28:12 +00:00
|
|
|
|
2019-05-01 15:45:18 +00:00
|
|
|
private ForkJoinPool forkJoinPoolPrimary = new ForkJoinPool();
|
|
|
|
private ForkJoinPool forkJoinPoolSecondary = new ForkJoinPool();
|
2019-08-18 01:09:09 +00:00
|
|
|
private ThreadPoolExecutor blockingExecutor = FaweCache.IMP.newBlockingExecutor();
|
2019-07-25 19:09:12 +00:00
|
|
|
private ConcurrentLinkedQueue<FutureTask> syncTasks = new ConcurrentLinkedQueue<>();
|
2019-04-28 17:36:23 +00:00
|
|
|
|
2019-08-18 01:09:09 +00:00
|
|
|
private Map<World, WeakReference<IChunkCache<IChunkGet>>> chunkCache = new HashMap<>();
|
|
|
|
private IterableThreadLocal<IQueueExtent> queuePool = new IterableThreadLocal<>(QueueHandler.this::create);
|
2019-07-11 15:41:54 +00:00
|
|
|
/**
|
2019-08-06 15:28:12 +00:00
|
|
|
* Used to calculate elapsed time in milliseconds and ensure block placement doesn't lag the
|
|
|
|
* server
|
2019-07-11 15:41:54 +00:00
|
|
|
*/
|
|
|
|
private long last;
|
|
|
|
private long allocate = 50;
|
|
|
|
private double targetTPS = 18;
|
2019-08-18 01:09:09 +00:00
|
|
|
|
2019-08-06 15:28:12 +00:00
|
|
|
public QueueHandler() {
|
|
|
|
TaskManager.IMP.repeat(this, 1);
|
|
|
|
}
|
2019-07-11 15:41:54 +00:00
|
|
|
|
2019-05-02 14:45:03 +00:00
|
|
|
@Override
|
|
|
|
public void run() {
|
|
|
|
if (!Fawe.isMainThread()) {
|
|
|
|
throw new IllegalStateException("Not main thread");
|
|
|
|
}
|
2019-07-11 15:41:54 +00:00
|
|
|
if (!syncTasks.isEmpty()) {
|
|
|
|
long now = System.currentTimeMillis();
|
|
|
|
targetTPS = 18 - Math.max(Settings.IMP.QUEUE.EXTRA_TIME_MS * 0.05, 0);
|
2019-08-06 15:28:12 +00:00
|
|
|
long diff = 50 + this.last - (this.last = now);
|
2019-07-11 15:41:54 +00:00
|
|
|
long absDiff = Math.abs(diff);
|
|
|
|
if (diff == 0) {
|
|
|
|
allocate = Math.min(50, allocate + 1);
|
|
|
|
} else if (diff < 0) {
|
|
|
|
allocate = Math.max(5, allocate + diff);
|
|
|
|
} else if (!Fawe.get().getTimer().isAbove(targetTPS)) {
|
|
|
|
allocate = Math.max(5, allocate - 1);
|
|
|
|
}
|
|
|
|
long currentAllocate = allocate - absDiff;
|
|
|
|
|
|
|
|
if (!MemUtil.isMemoryFree()) {
|
|
|
|
// TODO reduce mem usage
|
|
|
|
}
|
|
|
|
|
|
|
|
boolean wait = false;
|
|
|
|
do {
|
|
|
|
Runnable task = syncTasks.poll();
|
|
|
|
if (task == null) {
|
|
|
|
if (wait) {
|
|
|
|
synchronized (syncTasks) {
|
|
|
|
try {
|
|
|
|
syncTasks.wait(1);
|
|
|
|
} catch (InterruptedException e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
task = syncTasks.poll();
|
|
|
|
wait = false;
|
|
|
|
} else {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (task != null) {
|
|
|
|
task.run();
|
|
|
|
wait = true;
|
|
|
|
}
|
2019-10-03 23:35:55 +00:00
|
|
|
} while (System.currentTimeMillis() - now < currentAllocate);
|
2019-07-11 15:41:54 +00:00
|
|
|
}
|
2019-05-02 14:45:03 +00:00
|
|
|
while (!syncTasks.isEmpty()) {
|
|
|
|
final FutureTask task = syncTasks.poll();
|
2019-08-06 15:28:12 +00:00
|
|
|
if (task != null) {
|
|
|
|
task.run();
|
|
|
|
}
|
2019-05-02 14:45:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-18 16:07:31 +00:00
|
|
|
public <T extends Future<T>> void complete(Future<T> task) {
|
|
|
|
try {
|
|
|
|
while (task != null) {
|
|
|
|
task = task.get();
|
|
|
|
}
|
2019-07-25 19:09:12 +00:00
|
|
|
} catch (InterruptedException | ExecutionException e) {
|
2019-07-18 16:07:31 +00:00
|
|
|
e.printStackTrace();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-06 15:28:12 +00:00
|
|
|
public <T> Future<T> async(Runnable run, T value) {
|
2019-05-02 08:27:33 +00:00
|
|
|
return forkJoinPoolSecondary.submit(run, value);
|
|
|
|
}
|
|
|
|
|
2019-08-06 15:28:12 +00:00
|
|
|
public Future<?> async(Runnable run) {
|
2019-07-18 16:07:31 +00:00
|
|
|
return forkJoinPoolSecondary.submit(run);
|
|
|
|
}
|
|
|
|
|
2019-08-06 15:28:12 +00:00
|
|
|
public <T> Future<T> async(Callable<T> call) {
|
2019-05-02 08:27:33 +00:00
|
|
|
return forkJoinPoolSecondary.submit(call);
|
|
|
|
}
|
|
|
|
|
2019-08-06 15:28:12 +00:00
|
|
|
public ForkJoinTask submit(Runnable call) {
|
2019-07-11 15:32:14 +00:00
|
|
|
return forkJoinPoolPrimary.submit(call);
|
|
|
|
}
|
|
|
|
|
2019-08-06 15:28:12 +00:00
|
|
|
public <T> Future<T> sync(Runnable run, T value) {
|
2019-07-22 06:02:51 +00:00
|
|
|
if (Fawe.isMainThread()) {
|
|
|
|
run.run();
|
|
|
|
return Futures.immediateFuture(value);
|
|
|
|
}
|
2019-05-02 14:45:03 +00:00
|
|
|
final FutureTask<T> result = new FutureTask<>(run, value);
|
2019-05-02 08:27:33 +00:00
|
|
|
syncTasks.add(result);
|
2019-07-11 15:41:54 +00:00
|
|
|
notifySync();
|
2019-05-02 08:27:33 +00:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2019-08-06 15:28:12 +00:00
|
|
|
public <T> Future<T> sync(Runnable run) {
|
2019-07-22 06:02:51 +00:00
|
|
|
if (Fawe.isMainThread()) {
|
|
|
|
run.run();
|
|
|
|
return Futures.immediateCancelledFuture();
|
|
|
|
}
|
2019-05-02 14:45:03 +00:00
|
|
|
final FutureTask<T> result = new FutureTask<>(run, null);
|
2019-05-02 08:27:33 +00:00
|
|
|
syncTasks.add(result);
|
2019-07-11 15:41:54 +00:00
|
|
|
notifySync();
|
2019-05-02 08:27:33 +00:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2019-08-06 15:28:12 +00:00
|
|
|
public <T> Future<T> sync(Callable<T> call) throws Exception {
|
2019-07-22 06:02:51 +00:00
|
|
|
if (Fawe.isMainThread()) {
|
|
|
|
return Futures.immediateFuture(call.call());
|
|
|
|
}
|
2019-05-02 14:45:03 +00:00
|
|
|
final FutureTask<T> result = new FutureTask<>(call);
|
2019-05-02 08:27:33 +00:00
|
|
|
syncTasks.add(result);
|
2019-07-11 15:41:54 +00:00
|
|
|
notifySync();
|
2019-05-02 08:27:33 +00:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2019-08-06 15:28:12 +00:00
|
|
|
public <T> Future<T> sync(Supplier<T> call) {
|
2019-07-22 06:02:51 +00:00
|
|
|
if (Fawe.isMainThread()) {
|
|
|
|
return Futures.immediateFuture(call.get());
|
|
|
|
}
|
|
|
|
final FutureTask<T> result = new FutureTask<>(call::get);
|
|
|
|
syncTasks.add(result);
|
|
|
|
notifySync();
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2019-07-11 15:41:54 +00:00
|
|
|
private void notifySync() {
|
|
|
|
synchronized (syncTasks) {
|
|
|
|
syncTasks.notifyAll();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-06 15:28:12 +00:00
|
|
|
public <T extends Future<T>> T submit(IChunk<T> chunk) {
|
2019-07-22 06:02:51 +00:00
|
|
|
// if (MemUtil.isMemoryFree()) { TODO NOT IMPLEMENTED - optimize this
|
2019-05-01 15:45:18 +00:00
|
|
|
// return (T) forkJoinPoolSecondary.submit(chunk);
|
2019-07-22 06:02:51 +00:00
|
|
|
// }
|
2019-05-01 15:45:18 +00:00
|
|
|
return (T) blockingExecutor.submit(chunk);
|
2019-04-30 16:19:10 +00:00
|
|
|
}
|
|
|
|
|
2019-04-28 17:36:23 +00:00
|
|
|
/**
|
|
|
|
* Get or create the WorldChunkCache for a world
|
2019-08-06 15:28:12 +00:00
|
|
|
*
|
2019-04-28 17:36:23 +00:00
|
|
|
* @param world
|
|
|
|
* @return
|
|
|
|
*/
|
2019-08-18 01:09:09 +00:00
|
|
|
public IChunkCache<IChunkGet> getOrCreateWorldCache(World world) {
|
2019-04-28 17:36:23 +00:00
|
|
|
world = WorldWrapper.unwrap(world);
|
|
|
|
|
2019-04-28 15:44:59 +00:00
|
|
|
synchronized (chunkCache) {
|
2019-08-18 01:09:09 +00:00
|
|
|
final WeakReference<IChunkCache<IChunkGet>> ref = chunkCache.get(world);
|
2019-04-28 15:44:59 +00:00
|
|
|
if (ref != null) {
|
2019-08-18 01:09:09 +00:00
|
|
|
final IChunkCache<IChunkGet> cached = ref.get();
|
2019-04-28 15:44:59 +00:00
|
|
|
if (cached != null) {
|
|
|
|
return cached;
|
|
|
|
}
|
|
|
|
}
|
2019-08-18 01:09:09 +00:00
|
|
|
final IChunkCache<IChunkGet> created = new ChunkCache<>(world);
|
2019-04-28 15:44:59 +00:00
|
|
|
chunkCache.put(world, new WeakReference<>(created));
|
|
|
|
return created;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-26 04:45:03 +00:00
|
|
|
public IQueueExtent create() {
|
|
|
|
return new SingleThreadQueueExtent();
|
|
|
|
}
|
2019-04-28 15:44:59 +00:00
|
|
|
|
2019-07-22 09:05:14 +00:00
|
|
|
public abstract void startSet(boolean parallel);
|
2019-07-22 06:02:51 +00:00
|
|
|
|
2019-07-22 09:05:14 +00:00
|
|
|
public abstract void endSet(boolean parallel);
|
2019-07-22 06:02:51 +00:00
|
|
|
|
2019-08-06 15:28:12 +00:00
|
|
|
public IQueueExtent getQueue(World world) {
|
2019-05-02 14:45:03 +00:00
|
|
|
final IQueueExtent queue = queuePool.get();
|
2019-08-18 01:09:09 +00:00
|
|
|
IChunkCache<IChunkGet> cacheGet = getOrCreateWorldCache(world);
|
|
|
|
IChunkCache<IChunkSet> set = null; // TODO cache?
|
|
|
|
queue.init(world, cacheGet, set);
|
2019-04-30 16:19:10 +00:00
|
|
|
return queue;
|
|
|
|
}
|
|
|
|
|
2019-04-28 17:36:23 +00:00
|
|
|
@Override
|
2019-08-06 15:28:12 +00:00
|
|
|
public boolean trim(boolean aggressive) {
|
2019-04-28 15:44:59 +00:00
|
|
|
boolean result = true;
|
|
|
|
synchronized (chunkCache) {
|
2019-08-18 01:09:09 +00:00
|
|
|
final Iterator<Map.Entry<World, WeakReference<IChunkCache<IChunkGet>>>> iter = chunkCache
|
2019-08-06 15:28:12 +00:00
|
|
|
.entrySet().iterator();
|
2019-04-28 15:44:59 +00:00
|
|
|
while (iter.hasNext()) {
|
2019-08-18 01:09:09 +00:00
|
|
|
final Map.Entry<World, WeakReference<IChunkCache<IChunkGet>>> entry = iter.next();
|
|
|
|
final WeakReference<IChunkCache<IChunkGet>> value = entry.getValue();
|
|
|
|
final IChunkCache<IChunkGet> cache = value.get();
|
|
|
|
if (cache.trim(aggressive)) {
|
2019-04-28 15:44:59 +00:00
|
|
|
iter.remove();
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
result = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
2019-07-25 19:09:12 +00:00
|
|
|
}
|