Add new tick region scheduler

The old region scheduler had a few problems:
1. Inability to process intermediate tasks (chunk/packets) leading
   to higher than Paper/Vanilla latencies for task processing
2. Thread swapping: scheduled tasks had no thread preference, which
   lead to them swapping threads which may not be cache efficient

The new scheduler solves both of these issues. The new scheduler
can process intermediate tasks, and combined with the new packet
scheduler this allows packets to be processed at low latency
provided that the scheduler threads are idle or waiting.

The new scheduler will only move a scheduled tick/task to another
scheduling thread provided that its scheduled start is missed by
a specified time (for now, 2ms). This should ensure that thread
swapping only occurs to meet scheduling deadlines.
This commit is contained in:
Spottedleaf 2025-03-20 11:09:00 -07:00
parent 2986741444
commit 7452818d16
2 changed files with 2218 additions and 0 deletions

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,71 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
Date: Thu, 20 Mar 2025 11:01:42 -0700
Subject: [PATCH] fixup! Region Threading Base
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
index 6c2d9c9621f665412f1a8ccc41083fb0e3a07ed5..935ac76cec67ea661a392ff02396aa7aefd56268 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -3125,7 +3125,7 @@ public final class CraftServer implements Server {
@Override
public double[] getTPS() {
// Folia start - region threading
- ca.spottedleaf.concurrentutil.scheduler.SchedulerThreadPool.SchedulableTick task = io.papermc.paper.threadedregions.TickRegionScheduler.getCurrentTickingTask();
+ io.papermc.paper.threadedregions.ScheduledTaskThreadPool.SchedulableTick task = io.papermc.paper.threadedregions.TickRegionScheduler.getCurrentTickingTask();
if (task == null) {
// might be on the shutdown thread, try retrieving the current region
if (io.papermc.paper.threadedregions.TickRegionScheduler.getCurrentRegion() != null) {
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
index 71cb264e399d098f9e40c71258f1412aa0dbafc5..c53de765b624071cb4cd3fa69b5df4de5b95bf3b 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
@@ -229,6 +229,47 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
private boolean simplifyContainerDesyncCheck = GlobalConfiguration.get().unsupportedSettings.simplifyRemoteItemMatching;
private long lastSaveTime; // Paper - getLastPlayed replacement API
+ // Folia start - region threading
+ private final ca.spottedleaf.concurrentutil.collection.MultiThreadedQueue<Runnable> packetQueue = new ca.spottedleaf.concurrentutil.collection.MultiThreadedQueue<>();
+ // used only to notify tasks for packets
+ private volatile io.papermc.paper.threadedregions.RegionizedWorldData lastRegion;
+
+ public void stopAcceptingPackets() {
+ this.packetQueue.preventAdds();
+ }
+
+ public void updateRegion(final io.papermc.paper.threadedregions.RegionizedWorldData region) {
+ this.lastRegion = region;
+ if (region != null && this.hasPackets()) {
+ region.regionData.setHasPackets();
+ }
+ }
+
+ public boolean hasPackets() {
+ return !this.packetQueue.isEmpty();
+ }
+
+ public boolean executeOnePacket() {
+ final Runnable run = this.packetQueue.poll();
+ if (run != null) {
+ run.run();
+ return true;
+ }
+ return false;
+ }
+
+ public void addPacket(final Runnable runnable) {
+ if (!this.packetQueue.add(runnable)) {
+ return;
+ }
+
+ final io.papermc.paper.threadedregions.RegionizedWorldData region = this.lastRegion;
+ if (region != null) {
+ region.regionData.setHasPackets();
+ }
+ }
+ // Folia end - region threading
+
public CraftPlayer(CraftServer server, ServerPlayer entity) {
super(server, entity);