From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: MrHua269 Date: Wed, 18 Sep 2024 23:10:47 +0800 Subject: [PATCH] Add experimental config for folia scheduling issue fixing diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkTaskScheduler.java b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkTaskScheduler.java index 085d035b5d127d14af6b8487bee5446b814bc590..f79129ff781f4f30d31b7b15de5df9dc05060299 100644 --- a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkTaskScheduler.java +++ b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkTaskScheduler.java @@ -851,6 +851,29 @@ public final class ChunkTaskScheduler { return MinecraftServer.getServer().regionizedServer.taskQueue.createChunkTask(this.world, chunkX, chunkZ, run, priority); // Folia - regionised ticking } + // Luminol start + public PrioritisedExecutor.PrioritisedTask createScheduledChunkTask(final int chunkX, final int chunkZ, final Runnable run, + final PrioritisedExecutor.Priority priority) { + PrioritisedExecutor.PrioritisedTask created = MinecraftServer.getServer().regionizedServer.taskQueue.createChunkTask(this.world, chunkX, chunkZ, run, priority, true); + + boolean result; + for (;;) { + try { + result = created.queue(); + }catch (me.earthme.luminol.utils.TaskCancelledException ignored) { + return created; + } + + if (result) { + return created; + } + + created = MinecraftServer.getServer().regionizedServer.taskQueue.createChunkTask(this.world, chunkX, chunkZ, run, priority, true); + } + } + // Luminol end + + public PrioritisedExecutor.PrioritisedTask scheduleChunkTask(final int chunkX, final int chunkZ, final Runnable run) { return this.scheduleChunkTask(chunkX, chunkZ, run, PrioritisedExecutor.Priority.NORMAL); } diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/task/ChunkFullTask.java b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/task/ChunkFullTask.java index 8581b8771b8cc89ff7bdb63fbfd5628a1e7d4762..f0b053ba78b0e847499c99863984c6d52b5f024c 100644 --- a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/task/ChunkFullTask.java +++ b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/task/ChunkFullTask.java @@ -26,7 +26,8 @@ public final class ChunkFullTask extends ChunkProgressionTask implements Runnabl private final NewChunkHolder chunkHolder; private final ChunkAccess fromChunk; - private final PrioritisedExecutor.PrioritisedTask convertToFullTask; + private PrioritisedExecutor.PrioritisedTask convertToFullTask; // Luminol + private final PrioritisedExecutor.Priority priority; // Luminol public static final io.papermc.paper.util.IntervalledCounter chunkLoads = new io.papermc.paper.util.IntervalledCounter(java.util.concurrent.TimeUnit.SECONDS.toNanos(15L)); public static final io.papermc.paper.util.IntervalledCounter chunkGenerates = new io.papermc.paper.util.IntervalledCounter(java.util.concurrent.TimeUnit.SECONDS.toNanos(15L)); @@ -37,6 +38,7 @@ public final class ChunkFullTask extends ChunkProgressionTask implements Runnabl this.chunkHolder = chunkHolder; this.fromChunk = fromChunk; this.convertToFullTask = scheduler.createChunkTask(chunkX, chunkZ, this, priority); + this.priority = priority; // Luminol } @Override @@ -129,7 +131,12 @@ public final class ChunkFullTask extends ChunkProgressionTask implements Runnabl if ((boolean)SCHEDULED_HANDLE.getAndSet((ChunkFullTask)this, true)) { throw new IllegalStateException("Cannot double call schedule()"); } - this.convertToFullTask.queue(); + boolean queued = this.convertToFullTask.queue(); // Luminol + // Luminol start + if (!queued && me.earthme.luminol.config.modules.experiment.FoliaTaskQueueFixConfig.enabled) { + this.convertToFullTask = scheduler.createScheduledChunkTask(this.chunkX, this.chunkZ, this, this.priority); + } + // Luminol end } @Override diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/task/GenericDataLoadTask.java b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/task/GenericDataLoadTask.java index 7a65d351b448873c6f2c145c975c92be314b876c..8a403cbb17a5c94bb09104a7d236f008f78d0a8d 100644 --- a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/task/GenericDataLoadTask.java +++ b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/task/GenericDataLoadTask.java @@ -41,13 +41,15 @@ public abstract class GenericDataLoadTask { // processing off-main protected final PrioritisedExecutor.PrioritisedTask processOffMain; // processing on-main - protected final PrioritisedExecutor.PrioritisedTask processOnMain; + protected PrioritisedExecutor.PrioritisedTask processOnMain; // Luminol protected final ChunkTaskScheduler scheduler; protected final ServerLevel world; protected final int chunkX; protected final int chunkZ; protected final RegionFileIOThread.RegionFileType type; + private final PrioritisedExecutor.Priority priority; // Luminol + private ProcessOnMainTask mainTask; // Luminol public GenericDataLoadTask(final ChunkTaskScheduler scheduler, final ServerLevel world, final int chunkX, final int chunkZ, final RegionFileIOThread.RegionFileType type, @@ -61,6 +63,7 @@ public abstract class GenericDataLoadTask { final ProcessOnMainTask mainTask; if (this.hasOnMain()) { mainTask = new ProcessOnMainTask(); + this.mainTask = mainTask; this.processOnMain = this.createOnMain(mainTask, priority); } else { mainTask = null; @@ -81,6 +84,7 @@ public abstract class GenericDataLoadTask { } this.loadDataFromDiskTask = new LoadDataFromDiskTask(world, chunkX, chunkZ, type, new DataLoadCallback(offMainTask, mainTask), priority); + this.priority = priority; // Luminol } public static final record TaskResult(L left, R right) {} @@ -278,7 +282,19 @@ public abstract class GenericDataLoadTask { // no off-main task, so go straight to main this.onMainTask.data = (OnMain)compoundTag; this.onMainTask.throwable = throwable; - GenericDataLoadTask.this.processOnMain.queue(); + boolean queued = GenericDataLoadTask.this.processOnMain.queue(); // Luminol + + // Luminol start + if (!queued) { + if (!me.earthme.luminol.config.modules.experiment.FoliaTaskQueueFixConfig.enabled) { + return; + } + + if (GenericDataLoadTask.this.processOnMain instanceof io.papermc.paper.threadedregions.RegionizedTaskQueue.PrioritisedQueue.ChunkBasedPriorityTask) { + GenericDataLoadTask.this.processOnMain = scheduler.createScheduledChunkTask(GenericDataLoadTask.this.chunkX, GenericDataLoadTask.this.chunkZ, GenericDataLoadTask.this.mainTask, GenericDataLoadTask.this.priority); + } + } + // Luminol end } } catch (final Throwable thr2) { LOGGER.error("Failed I/O callback for task: " + GenericDataLoadTask.this.toString(), thr2); @@ -327,7 +343,19 @@ public abstract class GenericDataLoadTask { this.schedule.data = newData.left; this.schedule.throwable = newData.right; - GenericDataLoadTask.this.processOnMain.queue(); + boolean queued = GenericDataLoadTask.this.processOnMain.queue(); // Luminol + + // Luminol start + if (!queued) { + if (!me.earthme.luminol.config.modules.experiment.FoliaTaskQueueFixConfig.enabled) { + return; + } + + if (GenericDataLoadTask.this.processOnMain instanceof io.papermc.paper.threadedregions.RegionizedTaskQueue.PrioritisedQueue.ChunkBasedPriorityTask) { + GenericDataLoadTask.this.processOnMain = scheduler.createScheduledChunkTask(GenericDataLoadTask.this.chunkX, GenericDataLoadTask.this.chunkZ, GenericDataLoadTask.this.mainTask, GenericDataLoadTask.this.priority); + } + } + // Luminol end } else { GenericDataLoadTask.this.onComplete((TaskResult)newData); } diff --git a/src/main/java/io/papermc/paper/threadedregions/RegionizedTaskQueue.java b/src/main/java/io/papermc/paper/threadedregions/RegionizedTaskQueue.java index a1e1782d87403ca8934d37361be7ba66ddba133f..5f8758684ff0e3ae5d78b316d045205c59aaca69 100644 --- a/src/main/java/io/papermc/paper/threadedregions/RegionizedTaskQueue.java +++ b/src/main/java/io/papermc/paper/threadedregions/RegionizedTaskQueue.java @@ -21,6 +21,18 @@ public final class RegionizedTaskQueue { private static final TicketType TASK_QUEUE_TICKET = TicketType.create("task_queue_ticket", (a, b) -> 0); + // Luminol start + public PrioritisedExecutor.PrioritisedTask createChunkTask(final ServerLevel world, final int chunkX, final int chunkZ, + final Runnable run, boolean sft) { + return new PrioritisedQueue.ChunkBasedPriorityTask(world.taskQueueRegionData, chunkX, chunkZ, true, run, PrioritisedExecutor.Priority.NORMAL, sft); + } + + public PrioritisedExecutor.PrioritisedTask createChunkTask(final ServerLevel world, final int chunkX, final int chunkZ, + final Runnable run, final PrioritisedExecutor.Priority priority, boolean sft) { + return new PrioritisedQueue.ChunkBasedPriorityTask(world.taskQueueRegionData, chunkX, chunkZ, true, run, priority, sft); + } + // Luminol end + public PrioritisedExecutor.PrioritisedTask createChunkTask(final ServerLevel world, final int chunkX, final int chunkZ, final Runnable run) { return new PrioritisedQueue.ChunkBasedPriorityTask(world.taskQueueRegionData, chunkX, chunkZ, true, run, PrioritisedExecutor.Priority.NORMAL); @@ -48,8 +60,29 @@ public final class RegionizedTaskQueue { public PrioritisedExecutor.PrioritisedTask queueChunkTask(final ServerLevel world, final int chunkX, final int chunkZ, final Runnable run, final PrioritisedExecutor.Priority priority) { - final PrioritisedExecutor.PrioritisedTask ret = this.createChunkTask(world, chunkX, chunkZ, run, priority); - ret.queue(); + PrioritisedExecutor.PrioritisedTask ret = new PrioritisedQueue.ChunkBasedPriorityTask(world.taskQueueRegionData, chunkX, chunkZ, true, run, priority, me.earthme.luminol.config.modules.experiment.FoliaTaskQueueFixConfig.enabled); // Luminol + // Luminol start + if (!me.earthme.luminol.config.modules.experiment.FoliaTaskQueueFixConfig.enabled){ + ret.queue(); + return ret; + } + + for (;;) { + boolean result; + + try { + result = ret.queue(); + }catch (me.earthme.luminol.utils.TaskCancelledException ignored) { + result = true; + } + + if (result) { + break; + } + + ret = new PrioritisedQueue.ChunkBasedPriorityTask(world.taskQueueRegionData, chunkX, chunkZ, true, run, priority, me.earthme.luminol.config.modules.experiment.FoliaTaskQueueFixConfig.enabled); + } + // Luminol end return ret; } @@ -60,8 +93,29 @@ public final class RegionizedTaskQueue { public PrioritisedExecutor.PrioritisedTask queueTickTaskQueue(final ServerLevel world, final int chunkX, final int chunkZ, final Runnable run, final PrioritisedExecutor.Priority priority) { - final PrioritisedExecutor.PrioritisedTask ret = this.createTickTaskQueue(world, chunkX, chunkZ, run, priority); - ret.queue(); + PrioritisedExecutor.PrioritisedTask ret = new PrioritisedQueue.ChunkBasedPriorityTask(world.taskQueueRegionData, chunkX, chunkZ, false, run, priority, me.earthme.luminol.config.modules.experiment.FoliaTaskQueueFixConfig.enabled); // Luminol + // Luminol start + if (!me.earthme.luminol.config.modules.experiment.FoliaTaskQueueFixConfig.enabled){ + ret.queue(); + return ret; + } + + for (;;) { + boolean result; + + try { + result = ret.queue(); + }catch (me.earthme.luminol.utils.TaskCancelledException ignored) { + result = true; + } + + if (result) { + break; + } + + ret = new PrioritisedQueue.ChunkBasedPriorityTask(world.taskQueueRegionData, chunkX, chunkZ, false, run, priority, me.earthme.luminol.config.modules.experiment.FoliaTaskQueueFixConfig.enabled); + } + // Luminol end return ret; } @@ -136,6 +190,78 @@ public final class RegionizedTaskQueue { } } + // Luminol start - Try fixing scheduling + private final java.util.concurrent.locks.Lock referenceCountersLock = new java.util.concurrent.locks.ReentrantLock(true); + + private void luminolReplaced$decrementReference(final AtomicLong reference, final long coord) { + final int chunkX = CoordinateUtils.getChunkX(coord); + final int chunkZ = CoordinateUtils.getChunkZ(coord); + + this.referenceCountersLock.lock(); + boolean shouldRemove = false; + try { + final long val = reference.decrementAndGet(); + if (val == 0L) { + shouldRemove = this.referenceCounters.remove(coord, reference) == reference; + } else if (val < 0L) { + throw new IllegalStateException("Reference count < 0: " + val); + } + }finally { + this.referenceCountersLock.unlock(); + } + + if (shouldRemove) { + final ca.spottedleaf.concurrentutil.lock.ReentrantAreaLock.Node ticketLock = this.world.moonrise$getChunkTaskScheduler().chunkHolderManager.ticketLockArea.lock(chunkX, chunkZ); + try { + WorldRegionTaskData.this.removeTicket(coord); + } finally { + this.world.moonrise$getChunkTaskScheduler().chunkHolderManager.ticketLockArea.unlock(ticketLock); + } + } + } + + private AtomicLong luminolReplaced$incrementReference(final long coord){ + final AtomicLong result; + + final int chunkX = CoordinateUtils.getChunkX(coord); + final int chunkZ = CoordinateUtils.getChunkZ(coord); + + final boolean processTicketUpdates; + + final AtomicLong replace = new AtomicLong(1L); + + this.referenceCountersLock.lock(); + try { + final AtomicLong valueInMap = this.referenceCounters.putIfAbsent(coord, replace); + + if (valueInMap == null) { + result = replace; + processTicketUpdates = true; + }else { + processTicketUpdates = false; + valueInMap.getAndIncrement(); + result = valueInMap; + } + }finally { + this.referenceCountersLock.unlock(); + } + + if (processTicketUpdates) { + // Add the ticket so that we could create the tickregion + final ca.spottedleaf.concurrentutil.lock.ReentrantAreaLock.Node ticketLock = this.world.moonrise$getChunkTaskScheduler().chunkHolderManager.ticketLockArea.lock(chunkX, chunkZ); + try { + this.addTicket(coord); + } finally { + this.world.moonrise$getChunkTaskScheduler().chunkHolderManager.ticketLockArea.unlock(ticketLock); + } + + this.processTicketUpdates(coord); // Notify an update + } + + return result; + } + // Luminol end + private AtomicLong incrementReference(final long coord) { final AtomicLong ret = this.referenceCounters.get(coord); if (ret != null) { @@ -271,7 +397,7 @@ public final class RegionizedTaskQueue { } } - static final class PrioritisedQueue { + public static final class PrioritisedQueue { // Luminol private final ArrayDeque[] queues = new ArrayDeque[PrioritisedExecutor.Priority.TOTAL_SCHEDULABLE_PRIORITIES]; { for (int i = 0; i < PrioritisedExecutor.Priority.TOTAL_SCHEDULABLE_PRIORITIES; ++i) { this.queues[i] = new ArrayDeque<>(); @@ -428,13 +554,19 @@ public final class RegionizedTaskQueue { try { task.executeInternal(); } finally { - task.world.decrementReference(referenceCounter, task.sectionLowerLeftCoord); + // Luminol start - Try fixing schedule + if (me.earthme.luminol.config.modules.experiment.FoliaTaskQueueFixConfig.enabled) { + task.world.luminolReplaced$decrementReference(referenceCounter, task.sectionLowerLeftCoord); + }else { + task.world.decrementReference(referenceCounter, task.sectionLowerLeftCoord); + } + // Luminol end } return true; } - private static final class ChunkBasedPriorityTask implements PrioritisedExecutor.PrioritisedTask { + public static final class ChunkBasedPriorityTask implements PrioritisedExecutor.PrioritisedTask { // Luminol private static final AtomicLong REFERENCE_COUNTER_NOT_SET = new AtomicLong(-1L); @@ -449,6 +581,13 @@ public final class RegionizedTaskQueue { private Runnable run; private volatile PrioritisedExecutor.Priority priority; private static final VarHandle PRIORITY_HANDLE = ConcurrentUtil.getVarHandle(ChunkBasedPriorityTask.class, "priority", PrioritisedExecutor.Priority.class); + private boolean softThrowWhenCancelled = false; // Luminol + + ChunkBasedPriorityTask(final WorldRegionTaskData world, final int chunkX, final int chunkZ, final boolean isChunkTask, + final Runnable run, final PrioritisedExecutor.Priority priority, boolean sft) { // Luminol + this(world, chunkX, chunkZ, isChunkTask, run, priority); + this.softThrowWhenCancelled = sft; + } ChunkBasedPriorityTask(final WorldRegionTaskData world, final int chunkX, final int chunkZ, final boolean isChunkTask, final Runnable run, final PrioritisedExecutor.Priority priority) { @@ -533,7 +672,13 @@ public final class RegionizedTaskQueue { } } finally { if (curr != REFERENCE_COUNTER_NOT_SET) { - this.world.decrementReference(curr, this.sectionLowerLeftCoord); + // Luminol start - Try fixing scheduling + if (me.earthme.luminol.config.modules.experiment.FoliaTaskQueueFixConfig.enabled) { + this.world.luminolReplaced$decrementReference(curr, this.sectionLowerLeftCoord); + }else { + this.world.decrementReference(curr, this.sectionLowerLeftCoord); + } + // Luminol end } } @@ -547,10 +692,16 @@ public final class RegionizedTaskQueue { return false; } - final AtomicLong referenceCounter = this.world.incrementReference(this.sectionLowerLeftCoord); + final AtomicLong referenceCounter = me.earthme.luminol.config.modules.experiment.FoliaTaskQueueFixConfig.enabled ? this.world.luminolReplaced$incrementReference(this.sectionLowerLeftCoord) : this.world.incrementReference(this.sectionLowerLeftCoord); if (this.compareAndExchangeReferenceCounter(REFERENCE_COUNTER_NOT_SET, referenceCounter) != REFERENCE_COUNTER_NOT_SET) { // we don't expect race conditions here, so it is OK if we have to needlessly reference count - this.world.decrementReference(referenceCounter, this.sectionLowerLeftCoord); + // Luminol start - Try fixing scheduling + if (me.earthme.luminol.config.modules.experiment.FoliaTaskQueueFixConfig.enabled) { + this.world.luminolReplaced$decrementReference(referenceCounter, this.sectionLowerLeftCoord); + }else { + this.world.decrementReference(referenceCounter, this.sectionLowerLeftCoord); + } + // Luminol end return false; } @@ -574,6 +725,11 @@ public final class RegionizedTaskQueue { // the task never could be polled from the queue, so we return false // don't decrement reference count, as we were certainly cancelled by another thread, which // will decrement the reference count + // Luminol start + if (this.softThrowWhenCancelled) { + throw new me.earthme.luminol.utils.TaskCancelledException(); + } + // Luminol end return false; } @@ -584,6 +740,9 @@ public final class RegionizedTaskQueue { // we were cancelled // don't decrement reference count, as we were certainly cancelled by another thread, which // will decrement the reference count + if (this.softThrowWhenCancelled) { + throw new me.earthme.luminol.utils.TaskCancelledException(); + } return false; } diff --git a/src/main/java/me/earthme/luminol/config/modules/experiment/FoliaTaskQueueFixConfig.java b/src/main/java/me/earthme/luminol/config/modules/experiment/FoliaTaskQueueFixConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..47e0339ecb0d96010c41e739c66f3d024aad95eb --- /dev/null +++ b/src/main/java/me/earthme/luminol/config/modules/experiment/FoliaTaskQueueFixConfig.java @@ -0,0 +1,20 @@ +package me.earthme.luminol.config.modules.experiment; + +import me.earthme.luminol.config.ConfigInfo; +import me.earthme.luminol.config.EnumConfigCategory; +import me.earthme.luminol.config.IConfigModule; + +public class FoliaTaskQueueFixConfig implements IConfigModule { + @ConfigInfo(baseName = "enabled") + public static boolean enabled = false; + + @Override + public EnumConfigCategory getCategory() { + return EnumConfigCategory.EXPERIMENT; + } + + @Override + public String getBaseName() { + return "queue_until_task_queued"; + } +} diff --git a/src/main/java/me/earthme/luminol/utils/TaskCancelledException.java b/src/main/java/me/earthme/luminol/utils/TaskCancelledException.java new file mode 100644 index 0000000000000000000000000000000000000000..c7b55489f4d3a57320b0963e45cd1c87e6c0ec88 --- /dev/null +++ b/src/main/java/me/earthme/luminol/utils/TaskCancelledException.java @@ -0,0 +1,4 @@ +package me.earthme.luminol.utils; + +public class TaskCancelledException extends RuntimeException{ +}