mirror of
https://github.com/Minestom/Minestom.git
synced 2024-09-28 06:27:27 +02:00
Improve tick scheduling
This commit is contained in:
parent
997e35459e
commit
2e8b3477bf
@ -108,16 +108,8 @@ public final class UpdateManager {
|
|||||||
// Tick all instances
|
// Tick all instances
|
||||||
MinecraftServer.getInstanceManager().getInstances().forEach(instance ->
|
MinecraftServer.getInstanceManager().getInstances().forEach(instance ->
|
||||||
instance.tick(tickStart));
|
instance.tick(tickStart));
|
||||||
|
|
||||||
// Tick all chunks (and entities inside)
|
// Tick all chunks (and entities inside)
|
||||||
final CountDownLatch countDownLatch = threadProvider.update(tickStart);
|
this.threadProvider.updateAndAwait(tickStart);
|
||||||
|
|
||||||
// Wait tick end
|
|
||||||
try {
|
|
||||||
countDownLatch.await();
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clear removed entities & update threads
|
// Clear removed entities & update threads
|
||||||
long tickTime = System.currentTimeMillis() - tickStart;
|
long tickTime = System.currentTimeMillis() - tickStart;
|
||||||
|
@ -20,6 +20,8 @@ public interface Acquirable<T> {
|
|||||||
* Gets all the {@link Entity entities} being ticked in the current thread.
|
* Gets all the {@link Entity entities} being ticked in the current thread.
|
||||||
* <p>
|
* <p>
|
||||||
* Useful when you want to ensure that no acquisition is ever done.
|
* 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
|
* @return the entities ticked in the current thread
|
||||||
*/
|
*/
|
||||||
|
@ -11,7 +11,7 @@ import org.jetbrains.annotations.NotNull;
|
|||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.Phaser;
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
@ -30,6 +30,8 @@ public abstract class ThreadProvider {
|
|||||||
private final Set<Entity> updatableEntities = ConcurrentHashMap.newKeySet();
|
private final Set<Entity> updatableEntities = ConcurrentHashMap.newKeySet();
|
||||||
private final Set<Entity> removedEntities = ConcurrentHashMap.newKeySet();
|
private final Set<Entity> removedEntities = ConcurrentHashMap.newKeySet();
|
||||||
|
|
||||||
|
private final Phaser phaser = new Phaser(1);
|
||||||
|
|
||||||
public ThreadProvider(int threadCount) {
|
public ThreadProvider(int threadCount) {
|
||||||
this.threads = new ArrayList<>(threadCount);
|
this.threads = new ArrayList<>(threadCount);
|
||||||
|
|
||||||
@ -112,24 +114,23 @@ public abstract class ThreadProvider {
|
|||||||
*
|
*
|
||||||
* @param time the tick time in milliseconds
|
* @param time the tick time in milliseconds
|
||||||
*/
|
*/
|
||||||
public synchronized @NotNull CountDownLatch update(long time) {
|
public void updateAndAwait(long time) {
|
||||||
CountDownLatch countDownLatch = new CountDownLatch(threads.size());
|
for (var entry : threadChunkMap.entrySet()) {
|
||||||
for (TickThread thread : threads) {
|
final TickThread thread = entry.getKey();
|
||||||
|
final var chunkEntries = entry.getValue();
|
||||||
|
if (chunkEntries == null || chunkEntries.isEmpty()) {
|
||||||
|
// Nothing to tick
|
||||||
|
continue;
|
||||||
|
}
|
||||||
// Execute tick
|
// Execute tick
|
||||||
thread.runnable.startTick(countDownLatch, () -> {
|
this.phaser.register();
|
||||||
final var chunkEntries = threadChunkMap.get(thread);
|
thread.runnable.startTick(phaser, () -> {
|
||||||
if (chunkEntries == null || chunkEntries.isEmpty()) {
|
|
||||||
// Nothing to tick
|
|
||||||
Acquirable.refreshEntries(Collections.emptySet());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Acquirable.refreshEntries(chunkEntries);
|
Acquirable.refreshEntries(chunkEntries);
|
||||||
|
|
||||||
final ReentrantLock lock = thread.getLock();
|
final ReentrantLock lock = thread.getLock();
|
||||||
lock.lock();
|
lock.lock();
|
||||||
chunkEntries.forEach(chunkEntry -> {
|
chunkEntries.forEach(chunkEntry -> {
|
||||||
Chunk chunk = chunkEntry.chunk;
|
final Chunk chunk = chunkEntry.chunk;
|
||||||
if (!ChunkUtils.isLoaded(chunk))
|
if (!ChunkUtils.isLoaded(chunk))
|
||||||
return;
|
return;
|
||||||
chunk.tick(time);
|
chunk.tick(time);
|
||||||
@ -138,18 +139,18 @@ public abstract class ThreadProvider {
|
|||||||
for (Entity entity : entities) {
|
for (Entity entity : entities) {
|
||||||
if (lock.hasQueuedThreads()) {
|
if (lock.hasQueuedThreads()) {
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
// #acquire callbacks should be called here
|
// #acquire() callbacks should be called here
|
||||||
lock.lock();
|
lock.lock();
|
||||||
}
|
}
|
||||||
entity.tick(time);
|
entity.tick(time);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
Acquirable.refreshEntries(Collections.emptySet());
|
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
|
// #acquire() callbacks
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return countDownLatch;
|
this.phaser.arriveAndAwaitAdvance();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -4,7 +4,7 @@ import net.minestom.server.MinecraftServer;
|
|||||||
import net.minestom.server.utils.validate.Check;
|
import net.minestom.server.utils.validate.Check;
|
||||||
import org.jetbrains.annotations.NotNull;
|
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.atomic.AtomicReferenceFieldUpdater;
|
||||||
import java.util.concurrent.locks.LockSupport;
|
import java.util.concurrent.locks.LockSupport;
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
@ -60,16 +60,16 @@ public class TickThread extends Thread {
|
|||||||
// The context is necessary to control the tick rates
|
// The context is necessary to control the tick rates
|
||||||
if (localContext != null) {
|
if (localContext != null) {
|
||||||
// Execute tick
|
// Execute tick
|
||||||
localContext.runnable.run();
|
|
||||||
localContext.countDownLatch.countDown();
|
|
||||||
CONTEXT_UPDATER.compareAndSet(this, localContext, null);
|
CONTEXT_UPDATER.compareAndSet(this, localContext, null);
|
||||||
|
localContext.runnable.run();
|
||||||
|
localContext.phaser.arriveAndDeregister();
|
||||||
}
|
}
|
||||||
LockSupport.park(this);
|
LockSupport.park(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void startTick(@NotNull CountDownLatch countDownLatch, @NotNull Runnable runnable) {
|
protected void startTick(@NotNull Phaser phaser, @NotNull Runnable runnable) {
|
||||||
this.tickContext = new TickContext(countDownLatch, runnable);
|
this.tickContext = new TickContext(phaser, runnable);
|
||||||
LockSupport.unpark(tickThread);
|
LockSupport.unpark(tickThread);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -79,11 +79,11 @@ public class TickThread extends Thread {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static class TickContext {
|
private static class TickContext {
|
||||||
private final CountDownLatch countDownLatch;
|
private final Phaser phaser;
|
||||||
private final Runnable runnable;
|
private final Runnable runnable;
|
||||||
|
|
||||||
private TickContext(@NotNull CountDownLatch countDownLatch, @NotNull Runnable runnable) {
|
private TickContext(@NotNull Phaser phaser, @NotNull Runnable runnable) {
|
||||||
this.countDownLatch = countDownLatch;
|
this.phaser = phaser;
|
||||||
this.runnable = runnable;
|
this.runnable = runnable;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user