document some methods

This commit is contained in:
Jesse Boyd 2019-04-29 03:36:23 +10:00
parent 57b5be84f4
commit 6692a2eb92
No known key found for this signature in database
GPG Key ID: 59F1DE6293AF6E1F
19 changed files with 237 additions and 44 deletions

View File

@ -38,7 +38,7 @@ public class BukkitChunkHolder extends ChunkHolder<Boolean, BukkitQueue> {
}
@Override
public void filter(final Filter filter) {
public void set(final Filter filter) {
// for each block
// filter.applyBlock(block)
throw new UnsupportedOperationException("Not implemented");

View File

@ -21,7 +21,7 @@ public class BukkitFullChunk extends ChunkHolder {
}
@Override
public void filter(Filter filter) {
public void set(Filter filter) {
}

View File

@ -2,7 +2,10 @@ package com.boydti.fawe.beta;
import com.sk89q.worldedit.world.block.BaseBlock;
public interface Filter {
/**
* A filter is an interface used for setting blocks
*/
public interface Filter {
/**
* Check whether a chunk should be read
*
@ -47,4 +50,13 @@ public interface Filter {
*/
default void finishChunk(final IChunk chunk) {
}
/**
* Fork this for use by another thread
* - Typically filters are simple and don't need to create another copy to be thread safe here
* @return this
*/
default Filter fork() {
return this;
}
}

View File

@ -1,5 +1,8 @@
package com.boydti.fawe.beta;
/**
* Shared interface for IGetBlocks and ISetBlocks
*/
public interface IBlocks {
}
}

View File

@ -5,30 +5,59 @@ import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockStateHolder;
public interface IChunk<T, V extends IQueueExtent> {
/* set */
boolean setBiome(int x, int y, int z, BiomeType biome);
boolean setBlock(int x, int y, int z, BlockStateHolder block);
/* get */
BiomeType getBiome(int x, int z);
BlockState getBlock(int x, int y, int z);
BaseBlock getFullBlock(int x, int y, int z);
/**
* Represents a chunk in the queue {@link IQueueExtent}
* Used for getting and setting blocks / biomes / entities
* @param <T> The result type (typically returns true when the chunk is applied)
* @param <V> The IQueue class
*/
public interface IChunk<T, V extends IQueueExtent> extends Trimable {
/**
* Initialize at the location
* @param extent
* @param X
* @param Z
*/
void init(V extent, int X, int Z);
T apply();
int getX();
int getZ();
/**
* If the chunk is a delegate, returns it's paren'ts root
* @return root IChunk
*/
default IChunk getRoot() {
return this;
}
void filter(Filter filter);
/**
* @return true if no changes are queued for this chunk
*/
boolean isEmpty();
/**
* Apply the queued changes to the world
* @return
*/
T apply();
/* set - queues a change */
boolean setBiome(int x, int y, int z, BiomeType biome);
boolean setBlock(int x, int y, int z, BlockStateHolder block);
/**
* Set using the filter
* @param filter
*/
void set(Filter filter);
/* get - from the world */
BiomeType getBiome(int x, int z);
BlockState getBlock(int x, int y, int z);
BaseBlock getFullBlock(int x, int y, int z);
}

View File

@ -1,11 +1,16 @@
package com.boydti.fawe.beta;
import com.boydti.fawe.beta.implementation.SingleThreadQueueExtent;
import com.sk89q.worldedit.world.biome.BiomeType;
import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockStateHolder;
/**
* Delegate for IChunk
* @param <T> The result type (typically returns true when the chunk is applied)
* @param <V> The IQueue class
* @param <U> parent class
*/
public interface IDelegateChunk<T, V extends IQueueExtent, U extends IChunk<T, V>> extends IChunk<T, V> {
U getParent();
@ -57,11 +62,26 @@ public interface IDelegateChunk<T, V extends IQueueExtent, U extends IChunk<T, V
return getParent().getZ();
}
@Override
default boolean trim(boolean aggressive) {
return getParent().trim(aggressive);
}
/**
* Apply this chunk to the world
* @return result T (typically a boolean)
*/
@Override
default T apply() {
return getParent().apply();
}
@Override
default boolean isEmpty() {
return getParent().isEmpty();
}
default <T extends IChunk> T findParent(final Class<T> clazz) {
IChunk root = getParent();
if (clazz.isAssignableFrom(root.getClass())) return (T) root;
@ -73,7 +93,7 @@ public interface IDelegateChunk<T, V extends IQueueExtent, U extends IChunk<T, V
}
@Override
default void filter(Filter filter) {
getParent().filter(filter);
default void set(Filter filter) {
getParent().set(filter);
}
}

View File

@ -4,6 +4,9 @@ import com.boydti.fawe.beta.implementation.WorldChunkCache;
import java.util.concurrent.Future;
/**
* Delegate for IQueueExtent
*/
public interface IDelegateQueueExtent extends IQueueExtent {
IQueueExtent getParent();

View File

@ -4,12 +4,16 @@ import com.sk89q.worldedit.world.biome.BiomeType;
import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.world.block.BlockState;
public interface IGetBlocks extends IBlocks {
/**
* Interface for getting blocks
*/
public interface IGetBlocks extends IBlocks, Trimable {
BaseBlock getFullBlock(int x, int y, int z);
BiomeType getBiome(int x, int z);
BlockState getBlock(int x, int y, int z);
void trim();
@Override
boolean trim(boolean aggressive);
}

View File

@ -8,11 +8,27 @@ import com.sk89q.worldedit.world.block.BlockStateHolder;
import java.io.Flushable;
import java.util.concurrent.Future;
/**
* TODO: implement Extent (need to refactor Extent first)
* Interface for a queue based extent which uses chunks
*/
public interface IQueueExtent extends Flushable, Trimable {
void init(WorldChunkCache world);
/**
* Get the IChunk at a position (and cache it if it's not already)
* @param X
* @param Z
* @return IChunk
*/
IChunk getCachedChunk(int X, int Z);
/**
* Submit the chunk so that it's changes are applied to the world
* @param chunk
* @param <T> result type
* @return result
*/
<T> Future<T> submit(IChunk<T, ?> chunk);
default boolean setBlock(final int x, final int y, final int z, final BlockStateHolder state) {
@ -36,14 +52,16 @@ public interface IQueueExtent extends Flushable, Trimable {
}
/**
* Return the IChunk
* Create a new root IChunk object<br>
* - Full chunks will be reused, so a more optimized chunk can be returned in that case<br>
* - Don't wrap the chunk, that should be done in {@link #wrap(IChunk)}
* @param full
* @return
*/
IChunk create(boolean full);
/**
* Wrap the chunk object (i.e. for region restrictions etc.)
* Wrap the chunk object (i.e. for region restrictions / limits etc.)
* @param root
* @return wrapped chunk
*/
@ -51,6 +69,10 @@ public interface IQueueExtent extends Flushable, Trimable {
return root;
}
/**
* Flush all changes to the world
* - Best to call this async so it doesn't hang the server
*/
@Override
void flush();
}

View File

@ -3,8 +3,13 @@ package com.boydti.fawe.beta;
import com.sk89q.worldedit.world.biome.BiomeType;
import com.sk89q.worldedit.world.block.BlockStateHolder;
/**
* Interface for setting blocks
*/
public interface ISetBlocks extends IBlocks {
boolean setBiome(int x, int y, int z, BiomeType biome);
boolean setBlock(int x, int y, int z, BlockStateHolder holder);
boolean isEmpty();
}

View File

@ -1,5 +1,14 @@
package com.boydti.fawe.beta;
/**
* Interface for objects that can be trimmed (memory related)<br>
* - Trimming will reduce it's memory footprint
*/
public interface Trimable {
/**
* Trim the object, reducing it's memory footprint
* @param aggressive if trimming should be aggressive e.g. Not return early when the first element cannot be trimmed
* @return if this object is empty at the end of the trim, and can therefore be deleted
*/
boolean trim(boolean aggressive);
}

View File

@ -4,6 +4,7 @@ import com.boydti.fawe.beta.Filter;
import com.boydti.fawe.beta.IQueueExtent;
import com.boydti.fawe.beta.Trimable;
import com.boydti.fawe.object.collection.IterableThreadLocal;
import com.boydti.fawe.wrappers.WorldWrapper;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.world.World;
@ -12,8 +13,12 @@ import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
/**
* Class which handles all the queues {@link IQueueExtent}
*/
public abstract class QueueHandler implements Trimable {
private Map<World, WeakReference<WorldChunkCache>> chunkCache = new HashMap<>();
private IterableThreadLocal<IQueueExtent> pool = new IterableThreadLocal<IQueueExtent>() {
@Override
public IQueueExtent init() {
@ -21,7 +26,14 @@ public abstract class QueueHandler implements Trimable {
}
};
public WorldChunkCache getOrCreate(final World world) {
/**
* Get or create the WorldChunkCache for a world
* @param world
* @return
*/
public WorldChunkCache getOrCreate(World world) {
world = WorldWrapper.unwrap(world);
synchronized (chunkCache) {
final WeakReference<WorldChunkCache> ref = chunkCache.get(world);
if (ref != null) {
@ -38,6 +50,7 @@ public abstract class QueueHandler implements Trimable {
public abstract IQueueExtent create();
@Override
public boolean trim(final boolean aggressive) {
boolean result = true;
synchronized (chunkCache) {

View File

@ -6,6 +6,7 @@ import com.boydti.fawe.beta.implementation.holder.ReferenceChunk;
import com.boydti.fawe.config.Settings;
import com.boydti.fawe.util.MathMan;
import com.boydti.fawe.util.MemUtil;
import com.boydti.fawe.util.SetQueue;
import com.boydti.fawe.util.TaskManager;
import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap;
@ -16,20 +17,37 @@ import java.util.concurrent.ForkJoinTask;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Single threaded implementation for IQueueExtent (still abstract)
* - Does not implement creation of chunks (that has to implemented by the platform e.g. Bukkit)
*
* This queue is reusable {@link #init(WorldChunkCache)}
*/
public abstract class SingleThreadQueueExtent implements IQueueExtent {
private WorldChunkCache cache;
private Thread currentThread;
/**
* Safety check to ensure that the thread being used matches the one being initialized on
* - Can be removed later
*/
private void checkThread() {
if (Thread.currentThread() != currentThread && currentThread != null) {
throw new UnsupportedOperationException("This class must be used from a single thread. Use multiple queues for concurrent operations");
}
}
/**
* Get the {@link WorldChunkCache}
* @return
*/
public WorldChunkCache getCache() {
return cache;
}
/**
* Reset the queue
*/
protected synchronized void reset() {
checkThread();
cache = null;
@ -37,7 +55,7 @@ public abstract class SingleThreadQueueExtent implements IQueueExtent {
for (IChunk chunk : chunks.values()) {
chunk = chunk.getRoot();
if (chunk != null) {
chunkPool.add(chunk);
CHUNK_POOL.add(chunk);
}
}
chunks.clear();
@ -47,6 +65,10 @@ public abstract class SingleThreadQueueExtent implements IQueueExtent {
currentThread = null;
}
/**
* Initialize the queue
* @param cache
*/
@Override
public synchronized void init(final WorldChunkCache cache) {
if (cache != null) {
@ -57,24 +79,32 @@ public abstract class SingleThreadQueueExtent implements IQueueExtent {
this.cache = cache;
}
// Last access pointers
private IChunk lastChunk;
private long lastPair = Long.MAX_VALUE;
// Chunks currently being queued / worked on
private final Long2ObjectLinkedOpenHashMap<IChunk> chunks = new Long2ObjectLinkedOpenHashMap<>();
private final ConcurrentLinkedQueue<IChunk> chunkPool = new ConcurrentLinkedQueue<>();
// Pool discarded chunks for reuse (can safely be cleared by another thread)
private static final ConcurrentLinkedQueue<IChunk> CHUNK_POOL = new ConcurrentLinkedQueue<>();
@Override
public <T> ForkJoinTask<T> submit(final IChunk<T, ?> tmp) {
public <T> ForkJoinTask<T> submit(final IChunk<T, ?> chunk) {
if (chunk.isEmpty()) {
CHUNK_POOL.add(chunk);
return null;
}
// TODO use SetQueue to run in parallel
final ForkJoinPool pool = TaskManager.IMP.getPublicForkJoinPool();
return pool.submit(new Callable<T>() {
@Override
public T call() {
IChunk<T, ?> chunk = tmp;
IChunk<T, ?> tmp = chunk;
T result = chunk.apply();
T result = tmp.apply();
chunk = chunk.getRoot();
if (chunk != null) {
chunkPool.add(chunk);
tmp = tmp.getRoot();
if (tmp != null) {
CHUNK_POOL.add(tmp);
}
return result;
}
@ -83,7 +113,8 @@ public abstract class SingleThreadQueueExtent implements IQueueExtent {
@Override
public synchronized boolean trim(boolean aggressive) {
chunkPool.clear();
// TODO trim individial chunk sections
CHUNK_POOL.clear();
if (Thread.currentThread() == currentThread) {
lastChunk = null;
lastPair = Long.MAX_VALUE;
@ -94,13 +125,21 @@ public abstract class SingleThreadQueueExtent implements IQueueExtent {
}
}
private IChunk pool(final int X, final int Z) {
IChunk next = chunkPool.poll();
/**
* Get a new IChunk from either the pool, or create a new one<br>
* + Initialize it at the coordinates
* @param X
* @param Z
* @return IChunk
*/
private IChunk poolOrCreate(final int X, final int Z) {
IChunk next = CHUNK_POOL.poll();
if (next == null) next = create(false);
next.init(this, X, Z);
return next;
}
@Override
public final IChunk getCachedChunk(final int X, final int Z) {
final long pair = MathMan.pairInt(X, Z);
if (pair == lastPair) {
@ -125,7 +164,7 @@ public abstract class SingleThreadQueueExtent implements IQueueExtent {
submit(chunk);
}
}
chunk = pool(X, Z);
chunk = poolOrCreate(X, Z);
chunk = wrap(chunk);
chunks.put(pair, chunk);

View File

@ -10,6 +10,10 @@ import it.unimi.dsi.fastutil.objects.ObjectIterator;
import java.lang.ref.WeakReference;
import java.util.function.Supplier;
/**
* IGetBlocks may be cached by the WorldChunkCache so that it can be used between multiple IQueueExtents
* - avoids conversion between palette and raw data on every block get
*/
public class WorldChunkCache implements Trimable {
protected final Long2ObjectLinkedOpenHashMap<WeakReference<IGetBlocks>> getCache;
private final World world;
@ -27,6 +31,12 @@ public class WorldChunkCache implements Trimable {
return getCache.size();
}
/**
* Get or create the IGetBlocks
* @param index chunk index {@link com.boydti.fawe.util.MathMan#pairInt(int, int)}
* @param provider used to create if it isn't already cached
* @return cached IGetBlocks
*/
public synchronized IGetBlocks get(final long index, final Supplier<IGetBlocks> provider) {
final WeakReference<IGetBlocks> ref = getCache.get(index);
if (ref != null) {
@ -52,7 +62,7 @@ public class WorldChunkCache implements Trimable {
result = false;
if (!aggressive) return result;
synchronized (igb) {
igb.trim();
igb.trim(aggressive);
}
}
}

View File

@ -15,6 +15,9 @@ import com.sk89q.worldedit.world.block.BlockStateHolder;
import java.util.function.Supplier;
/**
* Abstract IChunk class that implements basic get/set blocks
*/
public abstract class ChunkHolder<T, V extends SingleThreadQueueExtent> implements IChunk<T, V>, Supplier<IGetBlocks> {
private IGetBlocks get;
private ISetBlocks set;
@ -30,6 +33,11 @@ public abstract class ChunkHolder<T, V extends SingleThreadQueueExtent> implemen
this.delegate = delegate;
}
@Override
public boolean isEmpty() {
return set == null || set.isEmpty();
}
public final IGetBlocks cachedGet() {
if (get == null) get = newGet();
return get;

View File

@ -3,6 +3,10 @@ package com.boydti.fawe.beta.implementation.holder;
import com.boydti.fawe.beta.IChunk;
import com.boydti.fawe.beta.IDelegateChunk;
/**
* Implementation of IDelegateChunk
* @param <T>
*/
public class DelegateChunk<T extends IChunk> implements IDelegateChunk {
private T parent;

View File

@ -6,6 +6,11 @@ import com.boydti.fawe.beta.IQueueExtent;
import java.lang.ref.Reference;
/**
* An IChunk may be wrapped by a ReferenceChunk if there is low memory<br>
* A reference chunk stores a reference (for garbage collection purposes)<br>
* - If it is garbage collected, the {@link FinalizedChunk} logic is run
*/
public abstract class ReferenceChunk implements IDelegateChunk {
private final Reference<FinalizedChunk> ref;

View File

@ -6,9 +6,12 @@ import com.boydti.fawe.beta.IQueueExtent;
import java.lang.ref.Reference;
import java.lang.ref.SoftReference;
/**
* Soft reference implementation of {@link ReferenceChunk}
*/
public class SoftChunk extends ReferenceChunk {
public SoftChunk(final IChunk parent, IQueueExtent queueExtent) {
public SoftChunk(final IChunk parent, final IQueueExtent queueExtent) {
super(parent, queueExtent);
}

View File

@ -1,13 +1,17 @@
package com.boydti.fawe.beta.implementation.holder;
import com.boydti.fawe.beta.IChunk;
import com.boydti.fawe.beta.IQueueExtent;
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
/**
* Weak reference implementation of {@link ReferenceChunk}
*/
public class WeakChunk extends ReferenceChunk {
public WeakChunk(final IChunk parent) {
super(parent);
public WeakChunk(final IChunk parent, final IQueueExtent queueExtent) {
super(parent, queueExtent);
}
@Override