Improve tick scheduling

This commit is contained in:
TheMode 2021-07-01 00:34:58 +02:00
parent 997e35459e
commit 2e8b3477bf
4 changed files with 28 additions and 33 deletions

View File

@ -108,16 +108,8 @@ public final class UpdateManager {
// Tick all instances
MinecraftServer.getInstanceManager().getInstances().forEach(instance ->
instance.tick(tickStart));
// Tick all chunks (and entities inside)
final CountDownLatch countDownLatch = threadProvider.update(tickStart);
// Wait tick end
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
this.threadProvider.updateAndAwait(tickStart);
// Clear removed entities & update threads
long tickTime = System.currentTimeMillis() - tickStart;

View File

@ -20,6 +20,8 @@ public interface Acquirable<T> {
* Gets all the {@link Entity entities} being ticked in the current thread.
* <p>
* Useful when you want to ensure that no acquisition is ever done.
* <p>
* Be aware that the entity stream is only updated at the beginning of the thread tick.
*
* @return the entities ticked in the current thread
*/

View File

@ -11,7 +11,7 @@ import org.jetbrains.annotations.NotNull;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Phaser;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Function;
@ -30,6 +30,8 @@ public abstract class ThreadProvider {
private final Set<Entity> updatableEntities = ConcurrentHashMap.newKeySet();
private final Set<Entity> removedEntities = ConcurrentHashMap.newKeySet();
private final Phaser phaser = new Phaser(1);
public ThreadProvider(int threadCount) {
this.threads = new ArrayList<>(threadCount);
@ -112,24 +114,23 @@ public abstract class ThreadProvider {
*
* @param time the tick time in milliseconds
*/
public synchronized @NotNull CountDownLatch update(long time) {
CountDownLatch countDownLatch = new CountDownLatch(threads.size());
for (TickThread thread : threads) {
public void updateAndAwait(long time) {
for (var entry : threadChunkMap.entrySet()) {
final TickThread thread = entry.getKey();
final var chunkEntries = entry.getValue();
if (chunkEntries == null || chunkEntries.isEmpty()) {
// Nothing to tick
continue;
}
// Execute tick
thread.runnable.startTick(countDownLatch, () -> {
final var chunkEntries = threadChunkMap.get(thread);
if (chunkEntries == null || chunkEntries.isEmpty()) {
// Nothing to tick
Acquirable.refreshEntries(Collections.emptySet());
return;
}
this.phaser.register();
thread.runnable.startTick(phaser, () -> {
Acquirable.refreshEntries(chunkEntries);
final ReentrantLock lock = thread.getLock();
lock.lock();
chunkEntries.forEach(chunkEntry -> {
Chunk chunk = chunkEntry.chunk;
final Chunk chunk = chunkEntry.chunk;
if (!ChunkUtils.isLoaded(chunk))
return;
chunk.tick(time);
@ -138,18 +139,18 @@ public abstract class ThreadProvider {
for (Entity entity : entities) {
if (lock.hasQueuedThreads()) {
lock.unlock();
// #acquire callbacks should be called here
// #acquire() callbacks should be called here
lock.lock();
}
entity.tick(time);
}
}
});
Acquirable.refreshEntries(Collections.emptySet());
lock.unlock();
// #acquire() callbacks
});
}
return countDownLatch;
this.phaser.arriveAndAwaitAdvance();
}
/**

View File

@ -4,7 +4,7 @@ import net.minestom.server.MinecraftServer;
import net.minestom.server.utils.validate.Check;
import org.jetbrains.annotations.NotNull;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Phaser;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
import java.util.concurrent.locks.LockSupport;
import java.util.concurrent.locks.ReentrantLock;
@ -60,16 +60,16 @@ public class TickThread extends Thread {
// The context is necessary to control the tick rates
if (localContext != null) {
// Execute tick
localContext.runnable.run();
localContext.countDownLatch.countDown();
CONTEXT_UPDATER.compareAndSet(this, localContext, null);
localContext.runnable.run();
localContext.phaser.arriveAndDeregister();
}
LockSupport.park(this);
}
}
protected void startTick(@NotNull CountDownLatch countDownLatch, @NotNull Runnable runnable) {
this.tickContext = new TickContext(countDownLatch, runnable);
protected void startTick(@NotNull Phaser phaser, @NotNull Runnable runnable) {
this.tickContext = new TickContext(phaser, runnable);
LockSupport.unpark(tickThread);
}
@ -79,11 +79,11 @@ public class TickThread extends Thread {
}
private static class TickContext {
private final CountDownLatch countDownLatch;
private final Phaser phaser;
private final Runnable runnable;
private TickContext(@NotNull CountDownLatch countDownLatch, @NotNull Runnable runnable) {
this.countDownLatch = countDownLatch;
private TickContext(@NotNull Phaser phaser, @NotNull Runnable runnable) {
this.phaser = phaser;
this.runnable = runnable;
}
}