From d1dc1a51b31c8a713e62fadf224e1669893a7f64 Mon Sep 17 00:00:00 2001 From: SirYwell Date: Wed, 28 Feb 2024 14:38:23 +0100 Subject: [PATCH 1/4] thread local extent --- .../implementation/ParallelQueueExtent.java | 5 ++- .../queue/implementation/QueueHandler.java | 12 +++++++ .../SingleThreadQueueExtent.java | 33 ++++++++++++++++- .../ThreadLocalPassthroughExtent.java | 35 +++++++++++++++++++ 4 files changed, 83 insertions(+), 2 deletions(-) create mode 100644 worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/ThreadLocalPassthroughExtent.java diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/ParallelQueueExtent.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/ParallelQueueExtent.java index 2d2b45aae9..704e0d5081 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/ParallelQueueExtent.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/ParallelQueueExtent.java @@ -42,7 +42,7 @@ import java.util.concurrent.ForkJoinTask; import java.util.stream.IntStream; -public class ParallelQueueExtent extends PassthroughExtent { +public class ParallelQueueExtent extends ThreadLocalPassthroughExtent { private static final Logger LOGGER = LogManagerCompat.getLogger(); @@ -114,6 +114,7 @@ public T apply(Region region, T filter, boolean full) { final SingleThreadQueueExtent queue = (SingleThreadQueueExtent) getNewQueue(); queue.setFastMode(fastmode); queue.setFaweExceptionArray(faweExceptionReasonsUsed); + enter(queue); synchronized (queue) { try { ChunkFilterBlock block = null; @@ -154,6 +155,8 @@ public T apply(Region region, T filter, boolean full) { exceptionCount++; LOGGER.warn(message); } + } finally { + exit(); } })).toArray(ForkJoinTask[]::new); // Join filters diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/QueueHandler.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/QueueHandler.java index 956c33fb2a..6690b20896 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/QueueHandler.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/QueueHandler.java @@ -18,9 +18,13 @@ import com.fastasyncworldedit.core.wrappers.WorldWrapper; import com.google.common.util.concurrent.Futures; import com.sk89q.worldedit.world.World; +import jdk.jfr.Category; +import jdk.jfr.Event; +import jdk.jfr.Name; import java.lang.ref.WeakReference; import java.util.HashMap; +import java.util.HexFormat; import java.util.Iterator; import java.util.Map; import java.util.Queue; @@ -412,10 +416,18 @@ public void unCache() { } private IQueueExtent pool() { + @Category("FAWE") + @Name("Request") + class RequestEvent extends Event { + String hash; + } + final RequestEvent requestEvent = new RequestEvent(); IQueueExtent queue = queuePool.get(); if (queue == null) { queuePool.set(queue = queuePool.init()); } + requestEvent.hash = HexFormat.of().toHexDigits(System.identityHashCode(queue)); + requestEvent.commit(); return queue; } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/SingleThreadQueueExtent.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/SingleThreadQueueExtent.java index 132229d1de..6ddaf784d5 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/SingleThreadQueueExtent.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/SingleThreadQueueExtent.java @@ -30,8 +30,14 @@ import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.world.World; import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap; +import jdk.jfr.Category; +import jdk.jfr.Event; +import jdk.jfr.Name; +import jdk.jfr.StackTrace; +import jdk.jfr.Threshold; import org.apache.logging.log4j.Logger; +import java.util.HexFormat; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; @@ -47,13 +53,22 @@ public class SingleThreadQueueExtent extends ExtentBatchProcessorHolder implements IQueueExtent { private static final Logger LOGGER = LogManagerCompat.getLogger(); + public final String hexDigits = HexFormat.of().toHexDigits(System.identityHashCode(this)); // Pool discarded chunks for reuse (can safely be cleared by another thread) // private static final ConcurrentLinkedQueue CHUNK_POOL = new ConcurrentLinkedQueue<>(); // Chunks currently being queued / worked on private final Long2ObjectLinkedOpenHashMap chunks = new Long2ObjectLinkedOpenHashMap<>(); private final ConcurrentLinkedQueue submissions = new ConcurrentLinkedQueue<>(); - private final ReentrantLock getChunkLock = new ReentrantLock(); + static class ObservableLock extends ReentrantLock { + + @Override + public Thread getOwner() { + return super.getOwner(); + } + + } + private final ObservableLock getChunkLock = new ObservableLock(); private World world = null; private int minY = 0; private int maxY = 255; @@ -290,6 +305,19 @@ private ChunkHolder poolOrCreate(int chunkX, int chunkZ) { @Override public final IQueueChunk getOrCreateChunk(int x, int z) { + @StackTrace(value = false) + @Category("FAWE") + @Name("Call") + class CallEvent extends Event { + String hash; + int queueLength; + Thread holder; + } + final CallEvent callEvent = new CallEvent(); + callEvent.hash = hexDigits; + callEvent.queueLength = getChunkLock.getQueueLength(); + callEvent.holder = getChunkLock.getOwner(); + callEvent.begin(); getChunkLock.lock(); try { final long pair = (long) x << 32 | z & 0xffffffffL; @@ -333,6 +361,9 @@ public final IQueueChunk getOrCreateChunk(int x, int z) { return chunk; } finally { getChunkLock.unlock(); + if (callEvent.holder != null) { + callEvent.commit(); + } } } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/ThreadLocalPassthroughExtent.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/ThreadLocalPassthroughExtent.java new file mode 100644 index 0000000000..3e2d2da921 --- /dev/null +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/ThreadLocalPassthroughExtent.java @@ -0,0 +1,35 @@ +package com.fastasyncworldedit.core.queue.implementation; + +import com.fastasyncworldedit.core.extent.PassthroughExtent; +import com.fastasyncworldedit.core.util.collection.CleanableThreadLocal; +import com.sk89q.worldedit.extent.Extent; + +public class ThreadLocalPassthroughExtent extends PassthroughExtent { + + private final CleanableThreadLocal localExtent; + + /** + * Create a new instance. + * + * @param extent the extent + */ + public ThreadLocalPassthroughExtent(final Extent extent) { + super(extent); + // fallback to one extent + this.localExtent = new CleanableThreadLocal<>(() -> extent); + } + + public void enter(Extent extent) { + this.localExtent.set(extent); + } + + public void exit() { + this.localExtent.remove(); + } + + @Override + public Extent getExtent() { + return this.localExtent.get(); + } + +} From 6ef46a073c5c9d86e4d10f7b850035f9265ac8f7 Mon Sep 17 00:00:00 2001 From: SirYwell Date: Thu, 29 Feb 2024 15:15:05 +0100 Subject: [PATCH 2/4] avoid race conditions due to ChunkHolder pooling --- .../fastasyncworldedit/core/FaweCache.java | 9 +++ .../queue/implementation/QueueHandler.java | 2 +- .../SingleThreadQueueExtent.java | 47 ++++++--------- .../ThreadLocalPassthroughExtent.java | 23 +++++--- .../implementation/chunk/ChunkHolder.java | 58 +++++++++++++++---- .../com/sk89q/worldedit/LocalSession.java | 5 ++ 6 files changed, 96 insertions(+), 48 deletions(-) diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/FaweCache.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/FaweCache.java index f5cc88284e..397ab3f403 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/FaweCache.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/FaweCache.java @@ -35,6 +35,9 @@ import com.sk89q.jnbt.Tag; import com.sk89q.worldedit.internal.util.LogManagerCompat; import com.sk89q.worldedit.world.block.BlockTypesCache; +import jdk.jfr.Category; +import jdk.jfr.Event; +import jdk.jfr.Name; import org.apache.logging.log4j.Logger; import javax.annotation.Nonnull; @@ -85,6 +88,12 @@ public enum FaweCache implements Trimable { @Override public synchronized boolean trim(boolean aggressive) { + @Category("FAWE") + @Name("ClearCache") + class ClearCacheEvent extends Event { + + } + new ClearCacheEvent().commit(); CHUNK_FLAG.clean(); BYTE_BUFFER_8192.clean(); BLOCK_TO_PALETTE.clean(); diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/QueueHandler.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/QueueHandler.java index 6690b20896..6528c7b180 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/QueueHandler.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/QueueHandler.java @@ -412,7 +412,7 @@ public IQueueExtent create() { * Sets the current thread's {@link IQueueExtent} instance in the queue pool to null. */ public void unCache() { - queuePool.set(null); + queuePool.remove(); } private IQueueExtent pool() { diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/SingleThreadQueueExtent.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/SingleThreadQueueExtent.java index 6ddaf784d5..92834dc526 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/SingleThreadQueueExtent.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/SingleThreadQueueExtent.java @@ -33,8 +33,6 @@ import jdk.jfr.Category; import jdk.jfr.Event; import jdk.jfr.Name; -import jdk.jfr.StackTrace; -import jdk.jfr.Threshold; import org.apache.logging.log4j.Logger; import java.util.HexFormat; @@ -49,17 +47,13 @@ *

* This queue is reusable {@link #init(Extent, IChunkCache, IChunkCache)} */ -@SuppressWarnings({"unchecked", "rawtypes"}) public class SingleThreadQueueExtent extends ExtentBatchProcessorHolder implements IQueueExtent { private static final Logger LOGGER = LogManagerCompat.getLogger(); - public final String hexDigits = HexFormat.of().toHexDigits(System.identityHashCode(this)); - // Pool discarded chunks for reuse (can safely be cleared by another thread) - // private static final ConcurrentLinkedQueue CHUNK_POOL = new ConcurrentLinkedQueue<>(); // Chunks currently being queued / worked on - private final Long2ObjectLinkedOpenHashMap chunks = new Long2ObjectLinkedOpenHashMap<>(); - private final ConcurrentLinkedQueue submissions = new ConcurrentLinkedQueue<>(); + private final Long2ObjectLinkedOpenHashMap> chunks = new Long2ObjectLinkedOpenHashMap<>(); + private final ConcurrentLinkedQueue> submissions = new ConcurrentLinkedQueue<>(); static class ObservableLock extends ReentrantLock { @Override @@ -157,12 +151,10 @@ protected synchronized void reset() { if (!this.initialized) { return; } - if (!this.chunks.isEmpty()) { - getChunkLock.lock(); - for (IChunk chunk : this.chunks.values()) { - chunk.recycle(); - } + getChunkLock.lock(); + try { this.chunks.clear(); + } finally { getChunkLock.unlock(); } this.enabledQueue = true; @@ -305,19 +297,6 @@ private ChunkHolder poolOrCreate(int chunkX, int chunkZ) { @Override public final IQueueChunk getOrCreateChunk(int x, int z) { - @StackTrace(value = false) - @Category("FAWE") - @Name("Call") - class CallEvent extends Event { - String hash; - int queueLength; - Thread holder; - } - final CallEvent callEvent = new CallEvent(); - callEvent.hash = hexDigits; - callEvent.queueLength = getChunkLock.getQueueLength(); - callEvent.holder = getChunkLock.getOwner(); - callEvent.begin(); getChunkLock.lock(); try { final long pair = (long) x << 32 | z & 0xffffffffL; @@ -351,6 +330,18 @@ class CallEvent extends Event { submissions.add(future); } } + @Category("FAWE") + @Name("ChunkRequest") + class ChunkRequestEvent extends Event { + int chunkX; + int chunkZ; + String hash; + } + final ChunkRequestEvent event = new ChunkRequestEvent(); + event.chunkX = x; + event.chunkZ = z; + event.hash = HexFormat.of().toHexDigits(System.identityHashCode(this)); + event.commit(); chunk = poolOrCreate(x, z); chunk = wrap(chunk); @@ -361,9 +352,9 @@ class CallEvent extends Event { return chunk; } finally { getChunkLock.unlock(); - if (callEvent.holder != null) { +/* if (callEvent.holder != null) { callEvent.commit(); - } + }*/ } } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/ThreadLocalPassthroughExtent.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/ThreadLocalPassthroughExtent.java index 3e2d2da921..4162f07e95 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/ThreadLocalPassthroughExtent.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/ThreadLocalPassthroughExtent.java @@ -1,12 +1,13 @@ package com.fastasyncworldedit.core.queue.implementation; import com.fastasyncworldedit.core.extent.PassthroughExtent; -import com.fastasyncworldedit.core.util.collection.CleanableThreadLocal; import com.sk89q.worldedit.extent.Extent; -public class ThreadLocalPassthroughExtent extends PassthroughExtent { +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; - private final CleanableThreadLocal localExtent; +public class ThreadLocalPassthroughExtent extends PassthroughExtent { + private static final ConcurrentMap extents = new ConcurrentHashMap<>(); /** * Create a new instance. @@ -15,21 +16,27 @@ public class ThreadLocalPassthroughExtent extends PassthroughExtent { */ public ThreadLocalPassthroughExtent(final Extent extent) { super(extent); - // fallback to one extent - this.localExtent = new CleanableThreadLocal<>(() -> extent); + } + + public static void clearCurrent() { + extents.remove(Thread.currentThread()); + } + + public static void setCurrentExtent(Extent extent) { + extents.put(Thread.currentThread(), extent); } public void enter(Extent extent) { - this.localExtent.set(extent); + extents.put(Thread.currentThread(), extent); } public void exit() { - this.localExtent.remove(); + extents.remove(Thread.currentThread()); } @Override public Extent getExtent() { - return this.localExtent.get(); + return extents.getOrDefault(Thread.currentThread(), super.getExtent()); } } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/chunk/ChunkHolder.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/chunk/ChunkHolder.java index 6103a1649c..30132850a0 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/chunk/ChunkHolder.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/chunk/ChunkHolder.java @@ -1,7 +1,5 @@ package com.fastasyncworldedit.core.queue.implementation.chunk; -import com.fastasyncworldedit.core.FaweCache; -import com.fastasyncworldedit.core.configuration.Settings; import com.fastasyncworldedit.core.extent.filter.block.ChunkFilterBlock; import com.fastasyncworldedit.core.extent.processor.EmptyBatchProcessor; import com.fastasyncworldedit.core.extent.processor.heightmap.HeightMapType; @@ -11,7 +9,7 @@ import com.fastasyncworldedit.core.queue.IChunkSet; import com.fastasyncworldedit.core.queue.IQueueChunk; import com.fastasyncworldedit.core.queue.IQueueExtent; -import com.fastasyncworldedit.core.queue.Pool; +import com.fastasyncworldedit.core.queue.implementation.ThreadLocalPassthroughExtent; import com.fastasyncworldedit.core.util.MemUtil; import com.sk89q.jnbt.CompoundTag; import com.sk89q.worldedit.math.BlockVector3; @@ -20,12 +18,16 @@ import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockStateHolder; +import jdk.jfr.Category; +import jdk.jfr.Event; +import jdk.jfr.Name; import javax.annotation.Nullable; import java.util.Map; import java.util.Set; import java.util.UUID; import java.util.concurrent.Future; +import java.util.concurrent.atomic.AtomicInteger; /** * An abstract {@link IChunk} class that implements basic get/set blocks. @@ -33,16 +35,13 @@ @SuppressWarnings("rawtypes") public class ChunkHolder> implements IQueueChunk { - private static final Pool POOL = FaweCache.INSTANCE.registerPool( - ChunkHolder.class, - ChunkHolder::new, - Settings.settings().QUEUE.POOL - ); - public static ChunkHolder newInstance() { - return POOL.poll(); + return new ChunkHolder(); } + private static final AtomicInteger ids = new AtomicInteger(); + + private final int id = ids.getAndIncrement(); private volatile IChunkGet chunkExisting; // The existing chunk (e.g. a clipboard, or the world, before changes) private volatile IChunkSet chunkSet; // The blocks to be set to the chunkExisting private IBlockDelegate delegate; // delegate handles the abstraction of the chunk layers @@ -56,6 +55,12 @@ public static ChunkHolder newInstance() { private long initTime = -1L; private ChunkHolder() { + @Category("FAWE") + @Name("CreateChunkHolder") + class CreateChunkHolderEvent extends Event { + int id = ChunkHolder.this.id; + } + new CreateChunkHolderEvent().commit(); this.delegate = NULL; } @@ -65,6 +70,7 @@ public void init(IBlockDelegate delegate) { @Override public synchronized void recycle() { + if (true) return; delegate = NULL; if (chunkSet != null) { chunkSet.recycle(); @@ -72,7 +78,12 @@ public synchronized void recycle() { } chunkExisting = null; extent = null; - POOL.offer(this); + @Category("FAWE") + @Name("Offer") + class OfferEvent extends Event { + public int id = ChunkHolder.this.id; + } + new OfferEvent().commit(); } public long initAge() { @@ -959,6 +970,17 @@ public boolean isEmpty() { */ public final IChunkGet getOrCreateGet() { if (chunkExisting == null) { + @Category("FAWE") + @Name("CreateGet") + class CreateGetEvent extends Event { + int id = ChunkHolder.this.id; + int chunkX; + int chunkZ; + } + final CreateGetEvent event = new CreateGetEvent(); + event.chunkX = chunkX; + event.chunkZ = chunkZ; + event.commit(); chunkExisting = newWrappedGet(); chunkExisting.trim(MemUtil.isMemoryLimited()); } @@ -1031,6 +1053,7 @@ public synchronized T call(IChunkSet set, Runnable finalize) { IChunkGet get = getOrCreateGet(); try { get.lockCall(); + trackExtent(); boolean postProcess = !(getExtent().getPostProcessor() instanceof EmptyBatchProcessor); final int copyKey = get.setCreateCopy(postProcess); final IChunkSet iChunkSet = getExtent().processSet(this, get, set); @@ -1046,11 +1069,24 @@ public synchronized T call(IChunkSet set, Runnable finalize) { return get.call(set, finalizer); } finally { get.unlockCall(); + untrackExtent(); } } return null; } + // "call" can be called by QueueHandler#blockingExecutor. In such case, we still want the other thread + // to use this SingleThreadQueueExtent. Otherwise, many threads might end up locking on **one** STQE. + // This way, locking is spread across multiple STQEs, allowing for better performance + + private void trackExtent() { + ThreadLocalPassthroughExtent.setCurrentExtent(extent); + } + + private void untrackExtent() { + ThreadLocalPassthroughExtent.clearCurrent(); + } + /** * Get the extent this chunk is in. */ diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/LocalSession.java b/worldedit-core/src/main/java/com/sk89q/worldedit/LocalSession.java index 67bbff7af9..a00b26702f 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/LocalSession.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/LocalSession.java @@ -32,6 +32,7 @@ import com.fastasyncworldedit.core.limit.FaweLimit; import com.fastasyncworldedit.core.util.BrushCache; import com.fastasyncworldedit.core.util.MainUtil; +import com.fastasyncworldedit.core.util.MaskTraverser; import com.fastasyncworldedit.core.util.StringMan; import com.fastasyncworldedit.core.util.TaskManager; import com.fastasyncworldedit.core.util.TextureHolder; @@ -53,6 +54,7 @@ import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.extension.platform.Locatable; +import com.sk89q.worldedit.extent.NullExtent; import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard; import com.sk89q.worldedit.extent.clipboard.Clipboard; import com.sk89q.worldedit.extent.inventory.BlockBag; @@ -594,6 +596,9 @@ public void remember(EditSession editSession, boolean append, int limitMb) { long size = MainUtil.getSize(item); historySize -= size; } + // free the mask from any remaining references to e.g. extents + // if used again + new MaskTraverser(mask).reset(NullExtent.INSTANCE); } finally { historyWriteLock.unlock(); } From 97dd7e6dabc3ffc6d94a71aeecbce42b925e940c Mon Sep 17 00:00:00 2001 From: SirYwell Date: Thu, 29 Feb 2024 15:36:59 +0100 Subject: [PATCH 3/4] clean up JFR events, javadoc --- .../fastasyncworldedit/core/FaweCache.java | 9 ---- .../queue/implementation/QueueHandler.java | 12 ----- .../SingleThreadQueueExtent.java | 32 +------------ .../ThreadLocalPassthroughExtent.java | 23 +++++++++ .../implementation/chunk/ChunkHolder.java | 47 ++++--------------- 5 files changed, 33 insertions(+), 90 deletions(-) diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/FaweCache.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/FaweCache.java index 397ab3f403..f5cc88284e 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/FaweCache.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/FaweCache.java @@ -35,9 +35,6 @@ import com.sk89q.jnbt.Tag; import com.sk89q.worldedit.internal.util.LogManagerCompat; import com.sk89q.worldedit.world.block.BlockTypesCache; -import jdk.jfr.Category; -import jdk.jfr.Event; -import jdk.jfr.Name; import org.apache.logging.log4j.Logger; import javax.annotation.Nonnull; @@ -88,12 +85,6 @@ public enum FaweCache implements Trimable { @Override public synchronized boolean trim(boolean aggressive) { - @Category("FAWE") - @Name("ClearCache") - class ClearCacheEvent extends Event { - - } - new ClearCacheEvent().commit(); CHUNK_FLAG.clean(); BYTE_BUFFER_8192.clean(); BLOCK_TO_PALETTE.clean(); diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/QueueHandler.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/QueueHandler.java index 6528c7b180..7bdbf46455 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/QueueHandler.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/QueueHandler.java @@ -18,13 +18,9 @@ import com.fastasyncworldedit.core.wrappers.WorldWrapper; import com.google.common.util.concurrent.Futures; import com.sk89q.worldedit.world.World; -import jdk.jfr.Category; -import jdk.jfr.Event; -import jdk.jfr.Name; import java.lang.ref.WeakReference; import java.util.HashMap; -import java.util.HexFormat; import java.util.Iterator; import java.util.Map; import java.util.Queue; @@ -416,18 +412,10 @@ public void unCache() { } private IQueueExtent pool() { - @Category("FAWE") - @Name("Request") - class RequestEvent extends Event { - String hash; - } - final RequestEvent requestEvent = new RequestEvent(); IQueueExtent queue = queuePool.get(); if (queue == null) { queuePool.set(queue = queuePool.init()); } - requestEvent.hash = HexFormat.of().toHexDigits(System.identityHashCode(queue)); - requestEvent.commit(); return queue; } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/SingleThreadQueueExtent.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/SingleThreadQueueExtent.java index 92834dc526..6e06ce3481 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/SingleThreadQueueExtent.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/SingleThreadQueueExtent.java @@ -9,7 +9,6 @@ import com.fastasyncworldedit.core.extent.processor.ExtentBatchProcessorHolder; import com.fastasyncworldedit.core.extent.processor.ProcessorScope; import com.fastasyncworldedit.core.internal.exception.FaweException; -import com.fastasyncworldedit.core.queue.IChunk; import com.fastasyncworldedit.core.queue.IChunkCache; import com.fastasyncworldedit.core.queue.IChunkGet; import com.fastasyncworldedit.core.queue.IChunkSet; @@ -30,12 +29,8 @@ import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.world.World; import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap; -import jdk.jfr.Category; -import jdk.jfr.Event; -import jdk.jfr.Name; import org.apache.logging.log4j.Logger; -import java.util.HexFormat; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; @@ -47,6 +42,7 @@ *

* This queue is reusable {@link #init(Extent, IChunkCache, IChunkCache)} */ +@SuppressWarnings({"unchecked", "rawtypes"}) public class SingleThreadQueueExtent extends ExtentBatchProcessorHolder implements IQueueExtent { private static final Logger LOGGER = LogManagerCompat.getLogger(); @@ -54,15 +50,7 @@ public class SingleThreadQueueExtent extends ExtentBatchProcessorHolder implemen // Chunks currently being queued / worked on private final Long2ObjectLinkedOpenHashMap> chunks = new Long2ObjectLinkedOpenHashMap<>(); private final ConcurrentLinkedQueue> submissions = new ConcurrentLinkedQueue<>(); - static class ObservableLock extends ReentrantLock { - - @Override - public Thread getOwner() { - return super.getOwner(); - } - - } - private final ObservableLock getChunkLock = new ObservableLock(); + private final ReentrantLock getChunkLock = new ReentrantLock(); private World world = null; private int minY = 0; private int maxY = 255; @@ -241,7 +229,6 @@ private > V submitUnchecked(IQueueChunk chunk) { } } if (chunk.isEmpty()) { - chunk.recycle(); Future result = Futures.immediateFuture(null); return (V) result; } @@ -330,18 +317,6 @@ public final IQueueChunk getOrCreateChunk(int x, int z) { submissions.add(future); } } - @Category("FAWE") - @Name("ChunkRequest") - class ChunkRequestEvent extends Event { - int chunkX; - int chunkZ; - String hash; - } - final ChunkRequestEvent event = new ChunkRequestEvent(); - event.chunkX = x; - event.chunkZ = z; - event.hash = HexFormat.of().toHexDigits(System.identityHashCode(this)); - event.commit(); chunk = poolOrCreate(x, z); chunk = wrap(chunk); @@ -352,9 +327,6 @@ class ChunkRequestEvent extends Event { return chunk; } finally { getChunkLock.unlock(); -/* if (callEvent.holder != null) { - callEvent.commit(); - }*/ } } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/ThreadLocalPassthroughExtent.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/ThreadLocalPassthroughExtent.java index 4162f07e95..e62135a3d3 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/ThreadLocalPassthroughExtent.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/ThreadLocalPassthroughExtent.java @@ -2,11 +2,34 @@ import com.fastasyncworldedit.core.extent.PassthroughExtent; import com.sk89q.worldedit.extent.Extent; +import org.jetbrains.annotations.ApiStatus; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; +/** + * This extent maintains a mapping from Threads to Extents. + * Whenever an implementation calls {@link #getExtent()}, it will get the extent + * associated with the current thread, or the one given by the super class if no mapping + * for this thread exist. + *

+ * There are two ways how to establish a mapping: + *

    + * by calling {@link #enter(Extent)}. + * This should be called paired with {@link #exit()} to clear the mapping again. + * by calling {@link #setCurrentExtent(Extent)}. + * This should be called paired with {@link #clearCurrent()} + *
+ * + * The first can be used when calling it in the context of a {@link ThreadLocalPassthroughExtent}. + * The static methods can be called from everywhere, but this requires extra attention to make sure no + * wrong mapping is kept. + * + * @since TODO + */ +@ApiStatus.Internal public class ThreadLocalPassthroughExtent extends PassthroughExtent { + private static final ConcurrentMap extents = new ConcurrentHashMap<>(); /** diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/chunk/ChunkHolder.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/chunk/ChunkHolder.java index 30132850a0..18c07c115e 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/chunk/ChunkHolder.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/chunk/ChunkHolder.java @@ -12,36 +12,33 @@ import com.fastasyncworldedit.core.queue.implementation.ThreadLocalPassthroughExtent; import com.fastasyncworldedit.core.util.MemUtil; import com.sk89q.jnbt.CompoundTag; +import com.sk89q.worldedit.internal.util.LogManagerCompat; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.Region; 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; -import jdk.jfr.Category; -import jdk.jfr.Event; -import jdk.jfr.Name; +import org.apache.logging.log4j.Logger; import javax.annotation.Nullable; import java.util.Map; import java.util.Set; import java.util.UUID; import java.util.concurrent.Future; -import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicBoolean; /** * An abstract {@link IChunk} class that implements basic get/set blocks. */ @SuppressWarnings("rawtypes") public class ChunkHolder> implements IQueueChunk { + private static final Logger LOGGER = LogManagerCompat.getLogger(); public static ChunkHolder newInstance() { return new ChunkHolder(); } - private static final AtomicInteger ids = new AtomicInteger(); - - private final int id = ids.getAndIncrement(); private volatile IChunkGet chunkExisting; // The existing chunk (e.g. a clipboard, or the world, before changes) private volatile IChunkSet chunkSet; // The blocks to be set to the chunkExisting private IBlockDelegate delegate; // delegate handles the abstraction of the chunk layers @@ -55,12 +52,6 @@ public static ChunkHolder newInstance() { private long initTime = -1L; private ChunkHolder() { - @Category("FAWE") - @Name("CreateChunkHolder") - class CreateChunkHolderEvent extends Event { - int id = ChunkHolder.this.id; - } - new CreateChunkHolderEvent().commit(); this.delegate = NULL; } @@ -68,22 +59,12 @@ public void init(IBlockDelegate delegate) { this.delegate = delegate; } + private static final AtomicBoolean recycleWarning = new AtomicBoolean(false); @Override - public synchronized void recycle() { - if (true) return; - delegate = NULL; - if (chunkSet != null) { - chunkSet.recycle(); - chunkSet = null; + public void recycle() { + if (!recycleWarning.getAndSet(true)) { + LOGGER.warn("ChunkHolder should not be recycled.", new Exception()); } - chunkExisting = null; - extent = null; - @Category("FAWE") - @Name("Offer") - class OfferEvent extends Event { - public int id = ChunkHolder.this.id; - } - new OfferEvent().commit(); } public long initAge() { @@ -970,17 +951,6 @@ public boolean isEmpty() { */ public final IChunkGet getOrCreateGet() { if (chunkExisting == null) { - @Category("FAWE") - @Name("CreateGet") - class CreateGetEvent extends Event { - int id = ChunkHolder.this.id; - int chunkX; - int chunkZ; - } - final CreateGetEvent event = new CreateGetEvent(); - event.chunkX = chunkX; - event.chunkZ = chunkZ; - event.commit(); chunkExisting = newWrappedGet(); chunkExisting.trim(MemUtil.isMemoryLimited()); } @@ -1040,7 +1010,6 @@ public synchronized T call() { // Do nothing }); } - recycle(); return null; } From e5f803bac4a4943926f60a5de65947cc7c3acad0 Mon Sep 17 00:00:00 2001 From: SirYwell Date: Sat, 2 Mar 2024 15:12:19 +0100 Subject: [PATCH 4/4] remove ThreadLocalPassthroughExtent --- .../implementation/ParallelQueueExtent.java | 32 ++++++++- .../ThreadLocalPassthroughExtent.java | 65 ------------------- .../implementation/chunk/ChunkHolder.java | 6 +- 3 files changed, 33 insertions(+), 70 deletions(-) delete mode 100644 worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/ThreadLocalPassthroughExtent.java diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/ParallelQueueExtent.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/ParallelQueueExtent.java index 704e0d5081..52e6cf2c68 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/ParallelQueueExtent.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/ParallelQueueExtent.java @@ -18,6 +18,7 @@ import com.fastasyncworldedit.core.queue.IQueueChunk; import com.fastasyncworldedit.core.queue.IQueueExtent; import com.sk89q.worldedit.MaxChangedBlocksException; +import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.extent.clipboard.Clipboard; import com.sk89q.worldedit.function.mask.BlockMask; import com.sk89q.worldedit.function.mask.ExistingBlockMask; @@ -42,9 +43,10 @@ import java.util.concurrent.ForkJoinTask; import java.util.stream.IntStream; -public class ParallelQueueExtent extends ThreadLocalPassthroughExtent { +public class ParallelQueueExtent extends PassthroughExtent { private static final Logger LOGGER = LogManagerCompat.getLogger(); + private static final ThreadLocal extents = new ThreadLocal<>(); private final World world; private final QueueHandler handler; @@ -73,10 +75,36 @@ public ParallelQueueExtent(QueueHandler handler, World world, boolean fastmode) this.fastmode = fastmode; } + /** + * Removes the extent currently associated with the calling thread. + */ + public static void clearCurrentExtent() { + extents.remove(); + } + + /** + * Sets the extent associated with the calling thread. + */ + public static void setCurrentExtent(Extent extent) { + extents.set(extent); + } + + private void enter(Extent extent) { + setCurrentExtent(extent); + } + + private void exit() { + clearCurrentExtent(); + } + @Override @SuppressWarnings({"unchecked", "rawtypes"}) public IQueueExtent getExtent() { - return (IQueueExtent) super.getExtent(); + Extent extent = extents.get(); + if (extent == null) { + extent = super.getExtent(); + } + return (IQueueExtent) extent; } @Override diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/ThreadLocalPassthroughExtent.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/ThreadLocalPassthroughExtent.java deleted file mode 100644 index e62135a3d3..0000000000 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/ThreadLocalPassthroughExtent.java +++ /dev/null @@ -1,65 +0,0 @@ -package com.fastasyncworldedit.core.queue.implementation; - -import com.fastasyncworldedit.core.extent.PassthroughExtent; -import com.sk89q.worldedit.extent.Extent; -import org.jetbrains.annotations.ApiStatus; - -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; - -/** - * This extent maintains a mapping from Threads to Extents. - * Whenever an implementation calls {@link #getExtent()}, it will get the extent - * associated with the current thread, or the one given by the super class if no mapping - * for this thread exist. - *

- * There are two ways how to establish a mapping: - *

    - * by calling {@link #enter(Extent)}. - * This should be called paired with {@link #exit()} to clear the mapping again. - * by calling {@link #setCurrentExtent(Extent)}. - * This should be called paired with {@link #clearCurrent()} - *
- * - * The first can be used when calling it in the context of a {@link ThreadLocalPassthroughExtent}. - * The static methods can be called from everywhere, but this requires extra attention to make sure no - * wrong mapping is kept. - * - * @since TODO - */ -@ApiStatus.Internal -public class ThreadLocalPassthroughExtent extends PassthroughExtent { - - private static final ConcurrentMap extents = new ConcurrentHashMap<>(); - - /** - * Create a new instance. - * - * @param extent the extent - */ - public ThreadLocalPassthroughExtent(final Extent extent) { - super(extent); - } - - public static void clearCurrent() { - extents.remove(Thread.currentThread()); - } - - public static void setCurrentExtent(Extent extent) { - extents.put(Thread.currentThread(), extent); - } - - public void enter(Extent extent) { - extents.put(Thread.currentThread(), extent); - } - - public void exit() { - extents.remove(Thread.currentThread()); - } - - @Override - public Extent getExtent() { - return extents.getOrDefault(Thread.currentThread(), super.getExtent()); - } - -} diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/chunk/ChunkHolder.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/chunk/ChunkHolder.java index 18c07c115e..a7417eddf5 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/chunk/ChunkHolder.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/chunk/ChunkHolder.java @@ -9,7 +9,7 @@ import com.fastasyncworldedit.core.queue.IChunkSet; import com.fastasyncworldedit.core.queue.IQueueChunk; import com.fastasyncworldedit.core.queue.IQueueExtent; -import com.fastasyncworldedit.core.queue.implementation.ThreadLocalPassthroughExtent; +import com.fastasyncworldedit.core.queue.implementation.ParallelQueueExtent; import com.fastasyncworldedit.core.util.MemUtil; import com.sk89q.jnbt.CompoundTag; import com.sk89q.worldedit.internal.util.LogManagerCompat; @@ -1049,11 +1049,11 @@ public synchronized T call(IChunkSet set, Runnable finalize) { // This way, locking is spread across multiple STQEs, allowing for better performance private void trackExtent() { - ThreadLocalPassthroughExtent.setCurrentExtent(extent); + ParallelQueueExtent.setCurrentExtent(extent); } private void untrackExtent() { - ThreadLocalPassthroughExtent.clearCurrent(); + ParallelQueueExtent.clearCurrentExtent(); } /**