3
0
Mirror von https://github.com/IntellectualSites/FastAsyncWorldEdit.git synchronisiert 2024-09-29 02:21:06 +02:00

feat: do not wait for chunk loads when calling

Dieser Commit ist enthalten in:
dordsor21 2024-09-14 18:32:37 +01:00
Ursprung b4635e85c9
Commit e161abea56
Es konnte kein GPG-Schlüssel zu dieser Signatur gefunden werden
GPG-Schlüssel-ID: 1E53E88969FFCF0B
20 geänderte Dateien mit 185 neuen und 79 gelöschten Zeilen

Datei anzeigen

@ -9,8 +9,10 @@ import com.fastasyncworldedit.core.extent.processor.heightmap.HeightMapType;
import com.fastasyncworldedit.core.math.BitArrayUnstretched;
import com.fastasyncworldedit.core.math.IntPair;
import com.fastasyncworldedit.core.nbt.FaweCompoundTag;
import com.fastasyncworldedit.core.queue.IChunk;
import com.fastasyncworldedit.core.queue.IChunkGet;
import com.fastasyncworldedit.core.queue.IChunkSet;
import com.fastasyncworldedit.core.queue.IQueueExtent;
import com.fastasyncworldedit.core.queue.implementation.QueueHandler;
import com.fastasyncworldedit.core.queue.implementation.blocks.CharGetBlocks;
import com.fastasyncworldedit.core.util.MathMan;
@ -423,7 +425,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
@Override
@SuppressWarnings("rawtypes")
public synchronized <T extends Future<T>> T call(IChunkSet set, Runnable finalizer) {
public synchronized <T extends Future<T>> T call(IQueueExtent<? extends IChunk> owner, IChunkSet set, Runnable finalizer) {
if (!callLock.isHeldByCurrentThread()) {
throw new IllegalStateException("Attempted to call chunk GET but chunk was not call-locked.");
}

Datei anzeigen

@ -3,8 +3,10 @@ package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R2;
import com.fastasyncworldedit.core.extent.processor.heightmap.HeightMapType;
import com.fastasyncworldedit.core.nbt.FaweCompoundTag;
import com.fastasyncworldedit.core.queue.IBlocks;
import com.fastasyncworldedit.core.queue.IChunk;
import com.fastasyncworldedit.core.queue.IChunkGet;
import com.fastasyncworldedit.core.queue.IChunkSet;
import com.fastasyncworldedit.core.queue.IQueueExtent;
import com.fastasyncworldedit.core.util.NbtUtils;
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
@ -254,7 +256,7 @@ public class PaperweightGetBlocks_Copy implements IChunkGet {
}
@Override
public <T extends Future<T>> T call(IChunkSet set, Runnable finalize) {
public <T extends Future<T>> T call(IQueueExtent<? extends IChunk> owner, IChunkSet set, Runnable finalize) {
return null;
}

Datei anzeigen

@ -9,8 +9,10 @@ import com.fastasyncworldedit.core.extent.processor.heightmap.HeightMapType;
import com.fastasyncworldedit.core.math.BitArrayUnstretched;
import com.fastasyncworldedit.core.math.IntPair;
import com.fastasyncworldedit.core.nbt.FaweCompoundTag;
import com.fastasyncworldedit.core.queue.IChunk;
import com.fastasyncworldedit.core.queue.IChunkGet;
import com.fastasyncworldedit.core.queue.IChunkSet;
import com.fastasyncworldedit.core.queue.IQueueExtent;
import com.fastasyncworldedit.core.queue.implementation.QueueHandler;
import com.fastasyncworldedit.core.queue.implementation.blocks.CharGetBlocks;
import com.fastasyncworldedit.core.util.MathMan;
@ -423,7 +425,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
@Override
@SuppressWarnings("rawtypes")
public synchronized <T extends Future<T>> T call(IChunkSet set, Runnable finalizer) {
public synchronized <T extends Future<T>> T call(IQueueExtent<? extends IChunk> owner, IChunkSet set, Runnable finalizer) {
if (!callLock.isHeldByCurrentThread()) {
throw new IllegalStateException("Attempted to call chunk GET but chunk was not call-locked.");
}

Datei anzeigen

@ -3,8 +3,10 @@ package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R3;
import com.fastasyncworldedit.core.extent.processor.heightmap.HeightMapType;
import com.fastasyncworldedit.core.nbt.FaweCompoundTag;
import com.fastasyncworldedit.core.queue.IBlocks;
import com.fastasyncworldedit.core.queue.IChunk;
import com.fastasyncworldedit.core.queue.IChunkGet;
import com.fastasyncworldedit.core.queue.IChunkSet;
import com.fastasyncworldedit.core.queue.IQueueExtent;
import com.fastasyncworldedit.core.util.NbtUtils;
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
@ -254,7 +256,7 @@ public class PaperweightGetBlocks_Copy implements IChunkGet {
}
@Override
public <T extends Future<T>> T call(IChunkSet set, Runnable finalize) {
public <T extends Future<T>> T call(IQueueExtent<? extends IChunk> owner, IChunkSet set, Runnable finalize) {
return null;
}

Datei anzeigen

@ -9,8 +9,10 @@ import com.fastasyncworldedit.core.extent.processor.heightmap.HeightMapType;
import com.fastasyncworldedit.core.math.BitArrayUnstretched;
import com.fastasyncworldedit.core.math.IntPair;
import com.fastasyncworldedit.core.nbt.FaweCompoundTag;
import com.fastasyncworldedit.core.queue.IChunk;
import com.fastasyncworldedit.core.queue.IChunkGet;
import com.fastasyncworldedit.core.queue.IChunkSet;
import com.fastasyncworldedit.core.queue.IQueueExtent;
import com.fastasyncworldedit.core.queue.implementation.QueueHandler;
import com.fastasyncworldedit.core.queue.implementation.blocks.CharGetBlocks;
import com.fastasyncworldedit.core.util.MathMan;
@ -424,7 +426,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
@Override
@SuppressWarnings("rawtypes")
public synchronized <T extends Future<T>> T call(IChunkSet set, Runnable finalizer) {
public synchronized <T extends Future<T>> T call(IQueueExtent<? extends IChunk> owner, IChunkSet set, Runnable finalizer) {
if (!callLock.isHeldByCurrentThread()) {
throw new IllegalStateException("Attempted to call chunk GET but chunk was not call-locked.");
}

Datei anzeigen

@ -3,8 +3,10 @@ package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R4;
import com.fastasyncworldedit.core.extent.processor.heightmap.HeightMapType;
import com.fastasyncworldedit.core.nbt.FaweCompoundTag;
import com.fastasyncworldedit.core.queue.IBlocks;
import com.fastasyncworldedit.core.queue.IChunk;
import com.fastasyncworldedit.core.queue.IChunkGet;
import com.fastasyncworldedit.core.queue.IChunkSet;
import com.fastasyncworldedit.core.queue.IQueueExtent;
import com.fastasyncworldedit.core.util.NbtUtils;
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
@ -257,7 +259,7 @@ public class PaperweightGetBlocks_Copy implements IChunkGet {
}
@Override
public <T extends Future<T>> T call(IChunkSet set, Runnable finalize) {
public <T extends Future<T>> T call(IQueueExtent<? extends IChunk> owner, IChunkSet set, Runnable finalize) {
return null;
}

Datei anzeigen

@ -6,11 +6,14 @@ import com.fastasyncworldedit.core.Fawe;
import com.fastasyncworldedit.core.FaweCache;
import com.fastasyncworldedit.core.configuration.Settings;
import com.fastasyncworldedit.core.extent.processor.heightmap.HeightMapType;
import com.fastasyncworldedit.core.internal.exception.FaweException;
import com.fastasyncworldedit.core.math.BitArrayUnstretched;
import com.fastasyncworldedit.core.math.IntPair;
import com.fastasyncworldedit.core.nbt.FaweCompoundTag;
import com.fastasyncworldedit.core.queue.IChunk;
import com.fastasyncworldedit.core.queue.IChunkGet;
import com.fastasyncworldedit.core.queue.IChunkSet;
import com.fastasyncworldedit.core.queue.IQueueExtent;
import com.fastasyncworldedit.core.queue.implementation.QueueHandler;
import com.fastasyncworldedit.core.queue.implementation.blocks.CharGetBlocks;
import com.fastasyncworldedit.core.util.MathMan;
@ -21,6 +24,7 @@ import com.sk89q.worldedit.bukkit.WorldEditPlugin;
import com.sk89q.worldedit.internal.Constants;
import com.sk89q.worldedit.internal.util.LogManagerCompat;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.util.formatting.text.TextComponent;
import com.sk89q.worldedit.world.biome.BiomeType;
import com.sk89q.worldedit.world.biome.BiomeTypes;
import com.sk89q.worldedit.world.block.BlockTypesCache;
@ -82,7 +86,9 @@ import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.Semaphore;
import java.util.concurrent.locks.ReadWriteLock;
@ -191,7 +197,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
try {
fillLightNibble(light, LightLayer.BLOCK, minSectionPosition, maxSectionPosition);
} catch (Throwable e) {
e.printStackTrace();
LOGGER.error("Error setting lighting to get", e);
}
}
}
@ -203,7 +209,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
try {
fillLightNibble(light, LightLayer.SKY, minSectionPosition, maxSectionPosition);
} catch (Throwable e) {
e.printStackTrace();
LOGGER.error("Error setting sky lighting to get", e);
}
}
}
@ -419,26 +425,42 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
entity.discard();
}
public LevelChunk ensureLoaded(ServerLevel nmsWorld, int chunkX, int chunkZ) {
public CompletableFuture<LevelChunk> ensureLoaded(ServerLevel nmsWorld, int chunkX, int chunkZ) {
return PaperweightPlatformAdapter.ensureLoaded(nmsWorld, chunkX, chunkZ);
}
@Override
@SuppressWarnings("rawtypes")
public synchronized <T extends Future<T>> T call(IChunkSet set, Runnable finalizer) {
public synchronized <T extends Future<T>> T call(IQueueExtent<? extends IChunk> owner, IChunkSet set, Runnable finalizer) {
if (!callLock.isHeldByCurrentThread()) {
throw new IllegalStateException("Attempted to call chunk GET but chunk was not call-locked.");
}
forceLoadSections = false;
LevelChunk nmsChunk = ensureLoaded(serverLevel, chunkX, chunkZ);
PaperweightGetBlocks_Copy copy = createCopy ? new PaperweightGetBlocks_Copy(nmsChunk) : null;
if (createCopy) {
if (copies.containsKey(copyKey)) {
throw new IllegalStateException("Copy key already used.");
}
copies.put(copyKey, copy);
final ServerLevel nmsWorld = serverLevel;
CompletableFuture<LevelChunk> nmsChunkFuture = ensureLoaded(nmsWorld, chunkX, chunkZ);
LevelChunk chunk = nmsChunkFuture.getNow(null);
// Run immediately if possible
if (chunk != null) {
return internalCall(set, finalizer, chunk, nmsWorld);
}
nmsChunkFuture.thenApply(nmsChunk -> owner.submitTaskUnchecked(() -> (T) internalCall(
set,
finalizer,
nmsChunk,
nmsWorld
)));
return (T) (Future) CompletableFuture.completedFuture(null);
}
private <T extends Future<T>> T internalCall(IChunkSet set, Runnable finalizer, LevelChunk nmsChunk, ServerLevel nmsWorld) {
try {
PaperweightGetBlocks_Copy copy = createCopy ? new PaperweightGetBlocks_Copy(nmsChunk) : null;
if (createCopy) {
if (copies.containsKey(copyKey)) {
throw new IllegalStateException("Copy key already used.");
}
copies.put(copyKey, copy);
}
// Remove existing tiles. Create a copy so that we can remove blocks
Map<BlockPos, BlockEntity> chunkTiles = new HashMap<>(nmsChunk.getBlockEntities());
List<BlockEntity> beacons = null;
@ -510,7 +532,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
biomeData
);
if (PaperweightPlatformAdapter.setSectionAtomic(
serverLevel.getWorld().getName(),
nmsWorld.getWorld().getName(),
chunkPos,
levelChunkSections,
null,
@ -586,7 +608,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
biomeData
);
if (PaperweightPlatformAdapter.setSectionAtomic(
serverLevel.getWorld().getName(),
nmsWorld.getWorld().getName(),
chunkPos,
levelChunkSections,
null,
@ -650,7 +672,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
biomeData != null ? biomeData : (PalettedContainer<Holder<Biome>>) existingSection.getBiomes()
);
if (!PaperweightPlatformAdapter.setSectionAtomic(
serverLevel.getWorld().getName(),
nmsWorld.getWorld().getName(),
chunkPos,
levelChunkSections,
existingSection,
@ -725,7 +747,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
}
if (Settings.settings().EXPERIMENTAL.REMOVE_ENTITY_FROM_WORLD_ON_CHUNK_FAIL) {
for (UUID uuid : entityRemoves) {
Entity entity = serverLevel.getEntities().get(uuid);
Entity entity = nmsWorld.getEntities().get(uuid);
if (entity != null) {
removeEntity(entity);
}
@ -764,7 +786,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
EntityType<?> type = EntityType.byString(id).orElse(null);
if (type != null) {
Entity entity = type.create(serverLevel);
Entity entity = type.create(nmsWorld);
if (entity != null) {
final CompoundTag tag = (CompoundTag) adapter.fromNativeLin(linTag);
for (final String name : Constants.NO_COPY_ENTITY_NBT_FIELDS) {
@ -773,11 +795,11 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
entity.load(tag);
entity.absMoveTo(x, y, z, yaw, pitch);
entity.setUUID(NbtUtils.uuid(nativeTag));
if (!serverLevel.addFreshEntity(entity, CreatureSpawnEvent.SpawnReason.CUSTOM)) {
if (!nmsWorld.addFreshEntity(entity, CreatureSpawnEvent.SpawnReason.CUSTOM)) {
LOGGER.warn(
"Error creating entity of type `{}` in world `{}` at location `{},{},{}`",
id,
serverLevel.getWorld().getName(),
nmsWorld.getWorld().getName(),
x,
y,
z
@ -807,11 +829,11 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
final int z = blockHash.z() + bz;
final BlockPos pos = new BlockPos(x, y, z);
synchronized (serverLevel) {
BlockEntity tileEntity = serverLevel.getBlockEntity(pos);
synchronized (nmsWorld) {
BlockEntity tileEntity = nmsWorld.getBlockEntity(pos);
if (tileEntity == null || tileEntity.isRemoved()) {
serverLevel.removeBlockEntity(pos);
tileEntity = serverLevel.getBlockEntity(pos);
nmsWorld.removeBlockEntity(pos);
tileEntity = nmsWorld.getBlockEntity(pos);
}
if (tileEntity != null) {
final CompoundTag tag = (CompoundTag) adapter.fromNativeLin(nativeTag.linTag());
@ -866,7 +888,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
return queueHandler.async(callback, null);
}
} catch (Throwable e) {
e.printStackTrace();
LOGGER.error("Error performing final chunk calling at {},{}", chunkX, chunkZ, e);
throw e;
}
};
@ -884,7 +906,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
}
return null;
} catch (Throwable e) {
e.printStackTrace();
LOGGER.error("Error calling chunk at {},{}", chunkX, chunkZ, e);
return null;
} finally {
forceLoadSections = true;
@ -1029,7 +1051,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
}
return data;
} catch (IllegalAccessException | InterruptedException e) {
e.printStackTrace();
LOGGER.error("Could not read block data from palette", e);
throw new RuntimeException(e);
} finally {
lock.release();
@ -1071,7 +1093,16 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
synchronized (this) {
levelChunk = this.levelChunk;
if (levelChunk == null) {
this.levelChunk = levelChunk = ensureLoaded(this.serverLevel, chunkX, chunkZ);
try {
this.levelChunk = levelChunk = ensureLoaded(this.serverLevel, chunkX, chunkZ).get();
} catch (InterruptedException | ExecutionException e) {
LOGGER.error("Could not get chunk at {},{}", chunkX, chunkZ, e);
throw new FaweException(
TextComponent.of("Could not get chunk at " + chunkX + "," + chunkZ + ": " + e.getMessage()),
FaweException.Type.OTHER,
false
);
}
}
}
}

Datei anzeigen

@ -3,8 +3,10 @@ package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_21_R1;
import com.fastasyncworldedit.core.extent.processor.heightmap.HeightMapType;
import com.fastasyncworldedit.core.nbt.FaweCompoundTag;
import com.fastasyncworldedit.core.queue.IBlocks;
import com.fastasyncworldedit.core.queue.IChunk;
import com.fastasyncworldedit.core.queue.IChunkGet;
import com.fastasyncworldedit.core.queue.IChunkSet;
import com.fastasyncworldedit.core.queue.IQueueExtent;
import com.fastasyncworldedit.core.util.NbtUtils;
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
@ -257,7 +259,7 @@ public class PaperweightGetBlocks_Copy implements IChunkGet {
}
@Override
public <T extends Future<T>> T call(IChunkSet set, Runnable finalize) {
public <T extends Future<T>> T call(IQueueExtent<? extends IChunk> owner, IChunkSet set, Runnable finalize) {
return null;
}

Datei anzeigen

@ -78,6 +78,8 @@ import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
@ -265,7 +267,43 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
}
}
public static LevelChunk ensureLoaded(ServerLevel serverLevel, int chunkX, int chunkZ) {
public static CompletableFuture<LevelChunk> ensureLoaded(ServerLevel serverLevel, int chunkX, int chunkZ) {
LevelChunk levelChunk = getChunkImmediatelyAsync(serverLevel, chunkX, chunkZ);
if (levelChunk != null) {
return CompletableFuture.completedFuture(levelChunk);
}
if (PaperLib.isPaper()) {
CompletableFuture<LevelChunk> future = serverLevel
.getWorld()
.getChunkAtAsync(chunkX, chunkZ, true, true)
.thenApply(chunk -> {
addTicket(serverLevel, chunkX, chunkZ);
try {
return (LevelChunk) CRAFT_CHUNK_GET_HANDLE.invoke(chunk);
} catch (Throwable e) {
LOGGER.error("Could not asynchronously load chunk at {},{}", chunkX, chunkZ, e);
return null;
}
});
try {
if (!future.isCompletedExceptionally() || (future.isDone() && future.get() != null)) {
return future;
}
Throwable t = future.exceptionNow();
LOGGER.error("Asynchronous chunk load at {},{} exceptionally completed immediately", chunkX, chunkZ, t);
} catch (InterruptedException | ExecutionException e) {
LOGGER.error(
"Unexpected error when getting completed future at chunk {},{}. Returning to default.",
chunkX,
chunkZ,
e
);
}
}
return CompletableFuture.supplyAsync(() -> TaskManager.taskManager().sync(() -> serverLevel.getChunk(chunkX, chunkZ)));
}
public static @Nullable LevelChunk getChunkImmediatelyAsync(ServerLevel serverLevel, int chunkX, int chunkZ) {
if (!PaperLib.isPaper()) {
LevelChunk nmsChunk = serverLevel.getChunkSource().getChunk(chunkX, chunkZ, false);
if (nmsChunk != null) {
@ -274,6 +312,7 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
if (Fawe.isMainThread()) {
return serverLevel.getChunk(chunkX, chunkZ);
}
return null;
} else {
LevelChunk nmsChunk = serverLevel.getChunkSource().getChunkAtIfCachedImmediately(chunkX, chunkZ);
if (nmsChunk != null) {
@ -289,30 +328,8 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
if (Fawe.isMainThread()) {
return serverLevel.getChunk(chunkX, chunkZ);
}
CompletableFuture<org.bukkit.Chunk> future = serverLevel.getWorld().getChunkAtAsync(chunkX, chunkZ, true, true);
try {
CraftChunk chunk;
try {
chunk = (CraftChunk) future.get(10, TimeUnit.SECONDS);
} catch (TimeoutException e) {
String world = serverLevel.getWorld().getName();
// We've already taken 10 seconds we can afford to wait a little here.
boolean loaded = TaskManager.taskManager().sync(() -> Bukkit.getWorld(world) != null);
if (loaded) {
LOGGER.warn("Chunk {},{} failed to load in 10 seconds in world {}. Retrying...", chunkX, chunkZ, world);
// Retry chunk load
chunk = (CraftChunk) serverLevel.getWorld().getChunkAtAsync(chunkX, chunkZ, true, true).get();
} else {
throw new UnsupportedOperationException("Cannot load chunk from unloaded world " + world + "!");
}
}
addTicket(serverLevel, chunkX, chunkZ);
return (LevelChunk) CRAFT_CHUNK_GET_HANDLE.invoke(chunk);
} catch (Throwable e) {
e.printStackTrace();
}
return null;
}
return TaskManager.taskManager().sync(() -> serverLevel.getChunk(chunkX, chunkZ));
}
private static void addTicket(ServerLevel serverLevel, int chunkX, int chunkZ) {

Datei anzeigen

@ -50,6 +50,7 @@ import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
@ -645,7 +646,7 @@ public enum FaweCache implements Trimable {
*/
public ThreadPoolExecutor newBlockingExecutor(String name, Logger logger) {
int nThreads = Settings.settings().QUEUE.PARALLEL_THREADS;
ArrayBlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(nThreads, true);
LinkedBlockingQueue<Runnable> queue = new LinkedBlockingQueue<>();
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS, queue,
new ThreadFactoryBuilder().setNameFormat(name).build(),

Datei anzeigen

@ -10,6 +10,7 @@ import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.world.block.BlockTypesCache;
import javax.annotation.Nullable;
import java.util.Arrays;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
@ -127,9 +128,7 @@ public interface IBatchProcessor {
char[] arr = set.loadIfPresent(layer);
if (arr != null) {
int index = (minY & 15) << 8;
for (int i = index; i < 4096; i++) {
arr[i] = BlockTypesCache.ReservedIDs.__RESERVED__;
}
Arrays.fill(arr, index, 4096, (char) BlockTypesCache.ReservedIDs.__RESERVED__);
}
set.setBlocks(layer, arr);
} else if (layer == maxLayer) {

Datei anzeigen

@ -45,7 +45,7 @@ public interface IChunkGet extends IBlocks, Trimable, InputExtent, ITileInput {
}
<T extends Future<T>> T call(IChunkSet set, Runnable finalize);
<T extends Future<T>> T call(IQueueExtent<? extends IChunk> owner, IChunkSet set, Runnable finalize);
@Deprecated(forRemoval = true, since = "2.11.2")
default CompoundTag getEntity(UUID uuid) {
@ -81,7 +81,7 @@ public interface IChunkGet extends IBlocks, Trimable, InputExtent, ITileInput {
}
/**
* Lock the {@link IChunkGet#call(IChunkSet, Runnable)} method to the current thread using a reentrant lock. Also locks
* Lock the {@link IChunkGet#call(IQueueExtent, IChunkSet, Runnable)} method to the current thread using a reentrant lock. Also locks
* related methods e.g. {@link IChunkGet#setCreateCopy(boolean)}
*
* @since 2.8.2
@ -89,7 +89,7 @@ public interface IChunkGet extends IBlocks, Trimable, InputExtent, ITileInput {
default void lockCall() {}
/**
* Unlock {@link IChunkGet#call(IChunkSet, Runnable)} (and other related methods) to executions from other threads
* Unlock {@link IChunkGet#call(IQueueExtent, IChunkSet, Runnable)} (and other related methods) to executions from other threads
*
* @since 2.8.2
*/

Datei anzeigen

@ -10,10 +10,12 @@ import com.sk89q.worldedit.function.operation.Operation;
import com.sk89q.worldedit.math.BlockVector2;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.regions.Region;
import org.jetbrains.annotations.ApiStatus;
import javax.annotation.Nullable;
import java.io.Flushable;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
/**
@ -61,7 +63,7 @@ public interface IQueueExtent<T extends IChunk> extends Flushable, Trimable, ICh
IChunkSet getCachedSet(int chunkX, int chunkZ);
/**
* Submit the chunk so that it's changes are applied to the world
* Submit the chunk so that its changes are applied to the world
*
* @return Future
*/
@ -81,6 +83,12 @@ public interface IQueueExtent<T extends IChunk> extends Flushable, Trimable, ICh
boolean isFastMode();
/**
* Submit a task to the extent to be queued as if it were a chunk
*/
@ApiStatus.Internal
<V extends Future<V>> V submitTaskUnchecked(Callable<V> callable);
/**
* Create a new root IChunk object. Full chunks will be reused, so a more optimized chunk can be
* returned in that case.

Datei anzeigen

@ -149,6 +149,8 @@ public class ParallelQueueExtent extends PassthroughExtent {
final SingleThreadQueueExtent queue = (SingleThreadQueueExtent) getNewQueue();
queue.setFastMode(fastmode);
queue.setFaweExceptionArray(faweExceptionReasonsUsed);
int div = ((size + 1) * 3) >> 1; // Allow each thread to use 1.5x TARGET_SIZE / PARALLEL_THREADS
queue.setTargetSize(Settings.settings().QUEUE.TARGET_SIZE / div);
enter(queue);
synchronized (queue) {
try {

Datei anzeigen

@ -18,6 +18,7 @@ import com.fastasyncworldedit.core.util.task.FaweForkJoinWorkerThreadFactory;
import com.fastasyncworldedit.core.wrappers.WorldWrapper;
import com.google.common.util.concurrent.Futures;
import com.sk89q.worldedit.world.World;
import org.jetbrains.annotations.ApiStatus;
import java.lang.ref.WeakReference;
import java.util.HashMap;
@ -41,14 +42,12 @@ import java.util.function.Supplier;
@SuppressWarnings({"unchecked", "rawtypes"})
public abstract class QueueHandler implements Trimable, Runnable {
private static final int PROCESSORS = Runtime.getRuntime().availableProcessors();
/**
* Primary queue should be used for tasks that are unlikely to wait on other tasks, IO, etc. (i.e. spend most of their
* time utilising CPU.
*/
private final ForkJoinPool forkJoinPoolPrimary = new ForkJoinPool(
PROCESSORS,
Settings.settings().QUEUE.PARALLEL_THREADS,
new FaweForkJoinWorkerThreadFactory("FAWE Fork Join Pool Primary - %s"),
null,
false
@ -59,7 +58,7 @@ public abstract class QueueHandler implements Trimable, Runnable {
* primary queue. They may be IO-bound tasks.
*/
private final ForkJoinPool forkJoinPoolSecondary = new ForkJoinPool(
PROCESSORS,
Settings.settings().QUEUE.PARALLEL_THREADS,
new FaweForkJoinWorkerThreadFactory("FAWE Fork Join Pool Secondary - %s"),
null,
false
@ -93,6 +92,11 @@ public abstract class QueueHandler implements Trimable, Runnable {
TaskManager.taskManager().repeat(this, 1);
}
@ApiStatus.Internal
public ThreadPoolExecutor getBlockingExecutor() {
return blockingExecutor;
}
@Override
public void run() {
if (!Fawe.isMainThread()) {
@ -380,6 +384,11 @@ public abstract class QueueHandler implements Trimable, Runnable {
return (T) blockingExecutor.submit(chunk);
}
@ApiStatus.Internal
public <T extends Future<T>> T submitToBlocking(Callable<T> callable) {
return (T) blockingExecutor.submit(callable);
}
/**
* Get or create the WorldChunkCache for a world
*/

Datei anzeigen

@ -31,6 +31,7 @@ import com.sk89q.worldedit.world.World;
import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap;
import org.apache.logging.log4j.Logger;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
@ -68,6 +69,7 @@ public class SingleThreadQueueExtent extends ExtentBatchProcessorHolder implemen
private boolean[] faweExceptionReasonsUsed = new boolean[FaweException.Type.values().length];
private int lastException = Integer.MIN_VALUE;
private int exceptionCount = 0;
private int targetSize = Settings.settings().QUEUE.TARGET_SIZE;
public SingleThreadQueueExtent() {
}
@ -120,6 +122,10 @@ public class SingleThreadQueueExtent extends ExtentBatchProcessorHolder implemen
return maxY;
}
public void setTargetSize(int targetSize) {
this.targetSize = targetSize;
}
/**
* Sets the cached boolean array of length {@code FaweException.Type.values().length} that determines if a thrown
* {@link FaweException} of type {@link FaweException.Type} should be output to console, rethrown to attempt to be visible
@ -154,6 +160,7 @@ public class SingleThreadQueueExtent extends ExtentBatchProcessorHolder implemen
this.setPostProcessor(EmptyBatchProcessor.getInstance());
this.world = null;
this.faweExceptionReasonsUsed = new boolean[FaweException.Type.values().length];
this.targetSize = Settings.settings().QUEUE.TARGET_SIZE;
}
/**
@ -246,6 +253,13 @@ public class SingleThreadQueueExtent extends ExtentBatchProcessorHolder implemen
return (V) Fawe.instance().getQueueHandler().submit(chunk);
}
@Override
public <V extends Future<V>> V submitTaskUnchecked(Callable<V> callable) {
V future = (V) Fawe.instance().getQueueHandler().submitToBlocking(callable);
submissions.add(future);
return future;
}
@Override
public synchronized boolean trim(boolean aggressive) {
cacheGet.trim(aggressive);
@ -308,7 +322,7 @@ public class SingleThreadQueueExtent extends ExtentBatchProcessorHolder implemen
// If queueing is enabled AND either of the following
// - memory is low & queue size > num threads + 8
// - queue size > target size and primary queue has less than num threads submissions
int targetSize = lowMem ? Settings.settings().QUEUE.PARALLEL_THREADS + 8 : Settings.settings().QUEUE.TARGET_SIZE;
int targetSize = lowMem ? Settings.settings().QUEUE.PARALLEL_THREADS + 8 : this.targetSize;
if (enabledQueue && size > targetSize && (lowMem || Fawe.instance().getQueueHandler().isUnderutilized())) {
chunk = chunks.removeFirst();
final Future future = submitUnchecked(chunk);

Datei anzeigen

@ -9,6 +9,8 @@ import com.sk89q.worldedit.world.block.BlockTypesCache;
import org.apache.logging.log4j.Logger;
import javax.annotation.Nullable;
import java.lang.invoke.VarHandle;
import java.util.Arrays;
public abstract class CharBlocks implements IBlocks {
@ -20,7 +22,10 @@ public abstract class CharBlocks implements IBlocks {
char[] arr = blocks.blocks[layer];
if (arr == null) {
// Chunk probably trimmed mid-operations, but do nothing about it to avoid other issues
return EMPTY.get(blocks, layer, false);
synchronized (blocks.sectionLocks[layer]) {
LOGGER.warn("Unexpected null section, please report this occurence alongside a debugpaste.");
return getSkipFull(blocks, layer, false);
}
}
return arr;
}
@ -32,6 +37,7 @@ public abstract class CharBlocks implements IBlocks {
if (arr == null) {
// Chunk probably trimmed mid-operations, but do nothing about it to avoid other issues
synchronized (blocks.sectionLocks[layer]) {
LOGGER.warn("Unexpected null section, please report this occurence alongside a debugpaste.");
return getSkipFull(blocks, layer, aggressive);
}
}
@ -118,6 +124,7 @@ public abstract class CharBlocks implements IBlocks {
public synchronized IChunkSet reset() {
for (int i = 0; i < sectionCount; i++) {
sections[i] = EMPTY;
VarHandle.storeStoreFence();
blocks[i] = null;
}
return null;
@ -134,9 +141,7 @@ public abstract class CharBlocks implements IBlocks {
if (data == null) {
return new char[4096];
}
for (int i = 0; i < 4096; i++) {
data[i] = defaultOrdinal();
}
Arrays.fill(data, defaultOrdinal());
return data;
}

Datei anzeigen

@ -4,8 +4,10 @@ import com.fastasyncworldedit.core.FaweCache;
import com.fastasyncworldedit.core.extent.processor.heightmap.HeightMapType;
import com.fastasyncworldedit.core.nbt.FaweCompoundTag;
import com.fastasyncworldedit.core.queue.IBlocks;
import com.fastasyncworldedit.core.queue.IChunk;
import com.fastasyncworldedit.core.queue.IChunkGet;
import com.fastasyncworldedit.core.queue.IChunkSet;
import com.fastasyncworldedit.core.queue.IQueueExtent;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.world.biome.BiomeType;
import com.sk89q.worldedit.world.biome.BiomeTypes;
@ -114,7 +116,7 @@ public final class NullChunkGet implements IChunkGet {
}
@Nullable
public <T extends Future<T>> T call(@Nonnull IChunkSet set, @Nonnull Runnable finalize) {
public <T extends Future<T>> T call(IQueueExtent<? extends IChunk> owner, @Nonnull IChunkSet set, @Nonnull Runnable finalize) {
return null;
}

Datei anzeigen

@ -987,7 +987,8 @@ public class ChunkHolder<T extends Future<T>> implements IQueueChunk<T> {
if (chunkSet != null && !chunkSet.isEmpty()) {
chunkSet.setBitMask(bitMask);
IChunkSet copy = chunkSet.createCopy();
return this.call(copy, () -> {
return this.call(extent, copy, () -> {
// Do nothing
});
}
@ -997,8 +998,9 @@ public class ChunkHolder<T extends Future<T>> implements IQueueChunk<T> {
/**
* This method should never be called from outside ChunkHolder
*/
@Override
public synchronized T call(IChunkSet set, Runnable finalize) {
public <U extends Future<U>> U call(IQueueExtent<? extends IChunk> owner, IChunkSet set, Runnable finalize) {
if (set != null) {
IChunkGet get = getOrCreateGet();
try {
@ -1016,7 +1018,7 @@ public class ChunkHolder<T extends Future<T>> implements IQueueChunk<T> {
} else {
finalizer = finalize;
}
return get.call(set, finalizer);
return get.call(extent, set, finalizer);
} finally {
get.unlockCall();
untrackExtent();

Datei anzeigen

@ -4,8 +4,10 @@ import com.fastasyncworldedit.core.extent.filter.block.ChunkFilterBlock;
import com.fastasyncworldedit.core.extent.processor.heightmap.HeightMapType;
import com.fastasyncworldedit.core.nbt.FaweCompoundTag;
import com.fastasyncworldedit.core.queue.Filter;
import com.fastasyncworldedit.core.queue.IChunk;
import com.fastasyncworldedit.core.queue.IChunkSet;
import com.fastasyncworldedit.core.queue.IQueueChunk;
import com.fastasyncworldedit.core.queue.IQueueExtent;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.world.biome.BiomeType;
@ -225,7 +227,7 @@ public final class NullChunk implements IQueueChunk {
}
@Nullable
public <T extends Future<T>> T call(@Nullable IChunkSet set, @Nullable Runnable finalize) {
public <T extends Future<T>> T call(IQueueExtent<? extends IChunk> owner, @Nullable IChunkSet set, @Nullable Runnable finalize) {
return null;
}