From e86b5a1c268c4670a10eab8ab87a9e22cec23753 Mon Sep 17 00:00:00 2001 From: Brendan Callanan Date: Thu, 28 Aug 2025 13:29:21 +0100 Subject: [PATCH 1/3] fix: resolve Folia queue synchronization issues in SingleThreadQueueExtent and FoliaTaskManager --- .../fastasyncworldedit/bukkit/util/FoliaTaskManager.java | 5 ++++- .../core/queue/implementation/SingleThreadQueueExtent.java | 6 +++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/util/FoliaTaskManager.java b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/util/FoliaTaskManager.java index 646fdcb17a..21b0fc9f18 100644 --- a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/util/FoliaTaskManager.java +++ b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/util/FoliaTaskManager.java @@ -128,9 +128,12 @@ public T syncWith(final Supplier supplier, final Player context) { @Override public T syncGlobal(final Supplier supplier) { - if (Bukkit.isPrimaryThread()) { + // FAWE start - Fix Folia compatibility: In Folia, there is no "primary thread" + // Instead, we need to check if we're on a tick thread and handle accordingly + if (FoliaSupport.isTickThread()) { return supplier.get(); } + // FAWE end final FutureTask task = new FutureTask<>(supplier::get); Bukkit.getGlobalRegionScheduler().run(WorldEditPlugin.getInstance(), asTickConsumer(task)); try { 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 3e7c8089c1..8487fe674d 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 @@ -37,6 +37,7 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.locks.ReentrantLock; +import com.fastasyncworldedit.core.util.FoliaSupport; /** * Single threaded implementation for IQueueExtent (still abstract) - Does not implement creation of @@ -254,7 +255,9 @@ private > V submitUnchecked(IQueueChunk chunk) { } } - if (Fawe.isTickThread()) { + // FAWE start - Fix Folia compatibility: Always submit to queue handler in Folia + // In Folia, direct execution on tick threads can cause synchronization issues + if (Fawe.isTickThread() && !FoliaSupport.isFolia()) { V result = (V) chunk.call(); if (result == null) { return (V) (Future) Futures.immediateFuture(null); @@ -262,6 +265,7 @@ private > V submitUnchecked(IQueueChunk chunk) { return result; } } + // FAWE end return (V) Fawe.instance().getQueueHandler().submit(chunk); } From e4d9717d88aa50812f56045f62acb7aacc761a8d Mon Sep 17 00:00:00 2001 From: R00tB33rMan Date: Thu, 28 Aug 2025 13:21:06 -0400 Subject: [PATCH 2/3] Adjust codestyle for adequate merge --- .../com/fastasyncworldedit/bukkit/util/FoliaTaskManager.java | 3 --- .../core/queue/implementation/SingleThreadQueueExtent.java | 3 --- 2 files changed, 6 deletions(-) diff --git a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/util/FoliaTaskManager.java b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/util/FoliaTaskManager.java index 21b0fc9f18..468cc8faf5 100644 --- a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/util/FoliaTaskManager.java +++ b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/util/FoliaTaskManager.java @@ -128,12 +128,9 @@ public T syncWith(final Supplier supplier, final Player context) { @Override public T syncGlobal(final Supplier supplier) { - // FAWE start - Fix Folia compatibility: In Folia, there is no "primary thread" - // Instead, we need to check if we're on a tick thread and handle accordingly if (FoliaSupport.isTickThread()) { return supplier.get(); } - // FAWE end final FutureTask task = new FutureTask<>(supplier::get); Bukkit.getGlobalRegionScheduler().run(WorldEditPlugin.getInstance(), asTickConsumer(task)); try { 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 8487fe674d..ca53aecba0 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 @@ -255,8 +255,6 @@ private > V submitUnchecked(IQueueChunk chunk) { } } - // FAWE start - Fix Folia compatibility: Always submit to queue handler in Folia - // In Folia, direct execution on tick threads can cause synchronization issues if (Fawe.isTickThread() && !FoliaSupport.isFolia()) { V result = (V) chunk.call(); if (result == null) { @@ -265,7 +263,6 @@ private > V submitUnchecked(IQueueChunk chunk) { return result; } } - // FAWE end return (V) Fawe.instance().getQueueHandler().submit(chunk); } From 5c697d5b01f0c3e9920bc71af414755212cd5afa Mon Sep 17 00:00:00 2001 From: Brendan Callanan Date: Fri, 5 Sep 2025 18:08:38 +0100 Subject: [PATCH 3/3] fix: not using vthreads and increasing timeout for folia --- .../changeset/ChangeExchangeCoordinator.java | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/changeset/ChangeExchangeCoordinator.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/changeset/ChangeExchangeCoordinator.java index 3c020d4221..30506e141b 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/changeset/ChangeExchangeCoordinator.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/changeset/ChangeExchangeCoordinator.java @@ -1,5 +1,6 @@ package com.fastasyncworldedit.core.history.changeset; +import com.fastasyncworldedit.core.util.FoliaSupport; import com.sk89q.worldedit.history.change.Change; import org.jetbrains.annotations.ApiStatus; @@ -30,12 +31,18 @@ public Change[] take(Change[] consumed) { if (!this.started) { this.started = true; final int length = consumed.length; - this.runner = UNDO_VIRTUAL_THREAD_BUILDER - .start(() -> this.runnerTask.accept(this.exchanger, new Change[length])); + if (FoliaSupport.isFolia()) { + this.runner = new Thread(() -> this.runnerTask.accept(this.exchanger, new Change[length]), "FAWE undo"); + this.runner.setDaemon(true); + this.runner.start(); + } else { + this.runner = UNDO_VIRTUAL_THREAD_BUILDER + .start(() -> this.runnerTask.accept(this.exchanger, new Change[length])); + } } try { - // Allow a reasonable timeout in case of weirdness - return exchanger.exchange(consumed, 30, TimeUnit.SECONDS); + long timeoutSeconds = FoliaSupport.isFolia() ? 60 : 30; + return exchanger.exchange(consumed, timeoutSeconds, TimeUnit.SECONDS); } catch (InterruptedException | TimeoutException e) { this.runner.interrupt(); Thread.currentThread().interrupt();