mirror of
https://github.com/Minestom/Minestom.git
synced 2025-01-01 05:58:00 +01:00
Remove Acquisition.java
This commit is contained in:
parent
fd17a63f7c
commit
b47946bfed
@ -1,7 +1,7 @@
|
||||
package net.minestom.server;
|
||||
|
||||
import com.google.common.collect.Queues;
|
||||
import net.minestom.server.acquirable.Acquisition;
|
||||
import net.minestom.server.acquirable.Acquirable;
|
||||
import net.minestom.server.instance.Chunk;
|
||||
import net.minestom.server.instance.Instance;
|
||||
import net.minestom.server.instance.InstanceManager;
|
||||
@ -86,12 +86,12 @@ public final class UpdateManager {
|
||||
|
||||
// Monitoring
|
||||
if (!tickMonitors.isEmpty()) {
|
||||
final double acquisitionTimeMs = Acquisition.getCurrentWaitMonitoring() / 1e6D;
|
||||
final double acquisitionTimeMs = Acquirable.getAcquiringTime() / 1e6D;
|
||||
final double tickTimeMs = tickTime / 1e6D;
|
||||
final TickMonitor tickMonitor = new TickMonitor(tickTimeMs, acquisitionTimeMs);
|
||||
this.tickMonitors.forEach(consumer -> consumer.accept(tickMonitor));
|
||||
|
||||
Acquisition.resetWaitMonitoring();
|
||||
Acquirable.resetAcquiringTime();
|
||||
}
|
||||
|
||||
// Flush all waiting packets
|
||||
|
@ -38,6 +38,25 @@ public interface Acquirable<T> {
|
||||
AcquirableImpl.CURRENT_ENTITIES.set(entities);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the time spent acquiring since last tick.
|
||||
*
|
||||
* @return the acquiring time
|
||||
*/
|
||||
static long getAcquiringTime() {
|
||||
return AcquirableImpl.WAIT_COUNTER_NANO.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets {@link #getAcquiringTime()}.
|
||||
* <p>
|
||||
* Mostly for internal use.
|
||||
*/
|
||||
@ApiStatus.Internal
|
||||
static void resetAcquiringTime() {
|
||||
AcquirableImpl.WAIT_COUNTER_NANO.set(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link Acquirable} object.
|
||||
* <p>
|
||||
@ -69,7 +88,7 @@ public interface Acquirable<T> {
|
||||
} else {
|
||||
final Thread currentThread = Thread.currentThread();
|
||||
final TickThread tickThread = getHandler().getTickThread();
|
||||
var lock = Acquisition.acquireEnter(currentThread, tickThread);
|
||||
var lock = Acquired.acquireEnter(currentThread, tickThread);
|
||||
return new Acquired<>(unwrap(), true, lock);
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,10 @@
|
||||
package net.minestom.server.acquirable;
|
||||
|
||||
import net.minestom.server.thread.TickThread;
|
||||
import net.minestom.server.utils.async.AsyncUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@ -17,7 +17,22 @@ public class AcquirableCollection<E> implements Collection<Acquirable<E>> {
|
||||
}
|
||||
|
||||
public void forEachSync(@NotNull Consumer<E> consumer) {
|
||||
Acquisition.acquireForEach(acquirableCollection, consumer);
|
||||
final Thread currentThread = Thread.currentThread();
|
||||
var threadEntitiesMap = retrieveOptionalThreadMap(acquirableCollection, currentThread, consumer);
|
||||
|
||||
// Acquire all the threads one by one
|
||||
{
|
||||
for (var entry : threadEntitiesMap.entrySet()) {
|
||||
final TickThread tickThread = entry.getKey();
|
||||
final List<E> values = entry.getValue();
|
||||
|
||||
var lock = Acquired.acquireEnter(currentThread, tickThread);
|
||||
for (E value : values) {
|
||||
consumer.accept(value);
|
||||
}
|
||||
Acquired.acquireLeave(lock);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void forEachAsync(@NotNull Consumer<E> consumer) {
|
||||
@ -95,4 +110,37 @@ public class AcquirableCollection<E> implements Collection<Acquirable<E>> {
|
||||
public void clear() {
|
||||
this.acquirableCollection.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates
|
||||
*
|
||||
* @param collection the acquirable collection
|
||||
* @param currentThread the current thread
|
||||
* @param consumer the consumer to execute when an element is already in the current thread
|
||||
* @return a new Thread to acquirable elements map
|
||||
*/
|
||||
protected static <T> Map<TickThread, List<T>> retrieveOptionalThreadMap(@NotNull Collection<Acquirable<T>> collection,
|
||||
@NotNull Thread currentThread,
|
||||
@NotNull Consumer<T> consumer) {
|
||||
// Separate a collection of acquirable elements into a map of thread->elements
|
||||
// Useful to reduce the number of acquisition
|
||||
|
||||
Map<TickThread, List<T>> threadCacheMap = new HashMap<>();
|
||||
for (var element : collection) {
|
||||
final T value = element.unwrap();
|
||||
|
||||
final TickThread elementThread = element.getHandler().getTickThread();
|
||||
if (currentThread == elementThread) {
|
||||
// The element is managed in the current thread, consumer can be immediately called
|
||||
consumer.accept(value);
|
||||
} else {
|
||||
// The element is manager in a different thread, cache it
|
||||
List<T> threadCacheList = threadCacheMap.computeIfAbsent(elementThread, tickThread -> new ArrayList<>());
|
||||
threadCacheList.add(value);
|
||||
}
|
||||
}
|
||||
|
||||
return threadCacheMap;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -3,11 +3,13 @@ package net.minestom.server.acquirable;
|
||||
import net.minestom.server.entity.Entity;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
class AcquirableImpl<T> implements Acquirable<T> {
|
||||
|
||||
protected static final ThreadLocal<Stream<Entity>> CURRENT_ENTITIES = ThreadLocal.withInitial(Stream::empty);
|
||||
protected static final AtomicLong WAIT_COUNTER_NANO = new AtomicLong();
|
||||
|
||||
private final T value;
|
||||
private final Acquirable.Handler handler;
|
||||
|
@ -1,5 +1,6 @@
|
||||
package net.minestom.server.acquirable;
|
||||
|
||||
import net.minestom.server.thread.TickThread;
|
||||
import net.minestom.server.utils.validate.Check;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@ -8,6 +9,11 @@ import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
public class Acquired<T> {
|
||||
|
||||
/**
|
||||
* Global lock used for synchronization.
|
||||
*/
|
||||
private static final ReentrantLock GLOBAL_LOCK = new ReentrantLock();
|
||||
|
||||
private final T value;
|
||||
|
||||
private final boolean locked;
|
||||
@ -32,7 +38,45 @@ public class Acquired<T> {
|
||||
this.unlocked = true;
|
||||
if (!locked)
|
||||
return;
|
||||
Acquisition.acquireLeave(lock);
|
||||
acquireLeave(lock);
|
||||
}
|
||||
|
||||
protected static @Nullable ReentrantLock acquireEnter(@Nullable Thread currentThread, @Nullable TickThread elementThread) {
|
||||
// Monitoring
|
||||
long time = System.nanoTime();
|
||||
|
||||
ReentrantLock currentLock;
|
||||
{
|
||||
final TickThread current = currentThread instanceof TickThread ?
|
||||
(TickThread) currentThread : null;
|
||||
currentLock = current != null && current.getLock().isHeldByCurrentThread() ?
|
||||
current.getLock() : null;
|
||||
}
|
||||
if (currentLock != null)
|
||||
currentLock.unlock();
|
||||
|
||||
GLOBAL_LOCK.lock();
|
||||
|
||||
if (currentLock != null)
|
||||
currentLock.lock();
|
||||
|
||||
final var lock = elementThread != null ? elementThread.getLock() : null;
|
||||
final boolean acquired = lock == null || lock.isHeldByCurrentThread();
|
||||
if (!acquired) {
|
||||
lock.lock();
|
||||
}
|
||||
|
||||
// Monitoring
|
||||
AcquirableImpl.WAIT_COUNTER_NANO.addAndGet(System.nanoTime() - time);
|
||||
|
||||
return !acquired ? lock : null;
|
||||
}
|
||||
|
||||
protected static void acquireLeave(@Nullable ReentrantLock lock) {
|
||||
if (lock != null) {
|
||||
lock.unlock();
|
||||
}
|
||||
GLOBAL_LOCK.unlock();
|
||||
}
|
||||
|
||||
private void checkLock() {
|
||||
|
@ -1,140 +0,0 @@
|
||||
package net.minestom.server.acquirable;
|
||||
|
||||
import net.minestom.server.thread.TickThread;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public final class Acquisition {
|
||||
|
||||
/**
|
||||
* Global lock used for synchronization.
|
||||
*/
|
||||
private static final ReentrantLock GLOBAL_LOCK = new ReentrantLock();
|
||||
|
||||
private static final AtomicLong WAIT_COUNTER_NANO = new AtomicLong();
|
||||
|
||||
/**
|
||||
* Acquires a {@link Collection}.
|
||||
* <p>
|
||||
* Order is not guaranteed.
|
||||
*
|
||||
* @param collection the collection to acquire
|
||||
* @param consumer the consumer called for each of the collection element
|
||||
*/
|
||||
protected static <T> void acquireForEach(@NotNull Collection<Acquirable<T>> collection,
|
||||
@NotNull Consumer<T> consumer) {
|
||||
final Thread currentThread = Thread.currentThread();
|
||||
var threadEntitiesMap = retrieveOptionalThreadMap(collection, currentThread, consumer);
|
||||
|
||||
// Acquire all the threads one by one
|
||||
{
|
||||
for (var entry : threadEntitiesMap.entrySet()) {
|
||||
final TickThread tickThread = entry.getKey();
|
||||
final List<T> values = entry.getValue();
|
||||
|
||||
acquire(currentThread, tickThread, () -> {
|
||||
for (T value : values) {
|
||||
consumer.accept(value);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that {@code callback} is safely executed inside the batch thread.
|
||||
*/
|
||||
protected static void acquire(@NotNull Thread currentThread, @Nullable TickThread elementThread,
|
||||
@NotNull Runnable callback) {
|
||||
if (Objects.equals(currentThread, elementThread)) {
|
||||
callback.run();
|
||||
} else {
|
||||
var lock = acquireEnter(currentThread, elementThread);
|
||||
callback.run();
|
||||
acquireLeave(lock);
|
||||
}
|
||||
}
|
||||
|
||||
protected static ReentrantLock acquireEnter(Thread currentThread, TickThread elementThread) {
|
||||
// Monitoring
|
||||
long time = System.nanoTime();
|
||||
|
||||
ReentrantLock currentLock;
|
||||
{
|
||||
final TickThread current = currentThread instanceof TickThread ?
|
||||
(TickThread) currentThread : null;
|
||||
currentLock = current != null && current.getLock().isHeldByCurrentThread() ?
|
||||
current.getLock() : null;
|
||||
}
|
||||
if (currentLock != null)
|
||||
currentLock.unlock();
|
||||
|
||||
GLOBAL_LOCK.lock();
|
||||
|
||||
if (currentLock != null)
|
||||
currentLock.lock();
|
||||
|
||||
final var lock = elementThread != null ? elementThread.getLock() : null;
|
||||
final boolean acquired = lock == null || lock.isHeldByCurrentThread();
|
||||
if (!acquired) {
|
||||
lock.lock();
|
||||
}
|
||||
|
||||
// Monitoring
|
||||
WAIT_COUNTER_NANO.addAndGet(System.nanoTime() - time);
|
||||
|
||||
return !acquired ? lock : null;
|
||||
}
|
||||
|
||||
protected static void acquireLeave(ReentrantLock lock) {
|
||||
if (lock != null) {
|
||||
lock.unlock();
|
||||
}
|
||||
GLOBAL_LOCK.unlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates
|
||||
*
|
||||
* @param collection the acquirable collection
|
||||
* @param currentThread the current thread
|
||||
* @param consumer the consumer to execute when an element is already in the current thread
|
||||
* @return a new Thread to acquirable elements map
|
||||
*/
|
||||
protected static <T> Map<TickThread, List<T>> retrieveOptionalThreadMap(@NotNull Collection<Acquirable<T>> collection,
|
||||
@NotNull Thread currentThread,
|
||||
@NotNull Consumer<T> consumer) {
|
||||
// Separate a collection of acquirable elements into a map of thread->elements
|
||||
// Useful to reduce the number of acquisition
|
||||
|
||||
Map<TickThread, List<T>> threadCacheMap = new HashMap<>();
|
||||
for (var element : collection) {
|
||||
final T value = element.unwrap();
|
||||
|
||||
final TickThread elementThread = element.getHandler().getTickThread();
|
||||
if (currentThread == elementThread) {
|
||||
// The element is managed in the current thread, consumer can be immediately called
|
||||
consumer.accept(value);
|
||||
} else {
|
||||
// The element is manager in a different thread, cache it
|
||||
List<T> threadCacheList = threadCacheMap.computeIfAbsent(elementThread, tickThread -> new ArrayList<>());
|
||||
threadCacheList.add(value);
|
||||
}
|
||||
}
|
||||
|
||||
return threadCacheMap;
|
||||
}
|
||||
|
||||
public static long getCurrentWaitMonitoring() {
|
||||
return WAIT_COUNTER_NANO.get();
|
||||
}
|
||||
|
||||
public static void resetWaitMonitoring() {
|
||||
WAIT_COUNTER_NANO.set(0);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user