package net.minestom.server.thread; import net.minestom.server.entity.Entity; import net.minestom.server.utils.async.AsyncUtils; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import java.util.Optional; import java.util.function.Consumer; import java.util.stream.Stream; @ApiStatus.Experimental public sealed interface Acquirable permits AcquirableImpl { /** * Gets all the {@link Entity entities} being ticked in the current thread. *

* Useful when you want to ensure that no acquisition is ever done. *

* Be aware that the entity stream is only updated at the beginning of the thread tick. * * @return the entities ticked in the current thread */ static @NotNull Stream<@NotNull Entity> currentEntities() { final Thread currentThread = Thread.currentThread(); if (currentThread instanceof TickThread) { return ((TickThread) currentThread).entries().stream() .flatMap(partitionEntry -> partitionEntry.elements().stream()) .filter(tickable -> tickable instanceof Entity) .map(tickable -> (Entity) tickable); } return Stream.empty(); } /** * Retrieve and reset acquiring time. */ @ApiStatus.Internal static long resetAcquiringTime() { return AcquirableImpl.WAIT_COUNTER_NANO.getAndSet(0); } /** * Creates a new {@link Acquirable} object. *

* Mostly for internal use, as a {@link TickThread} has to be used * and properly synchronized. * * @param value the acquirable element * @param the acquirable element type * @return a new acquirable object */ @ApiStatus.Internal static @NotNull Acquirable of(@NotNull T value) { return new AcquirableImpl<>(value); } /** * Returns a new {@link Acquired} object which will be locked to the current thread. *

* Useful when your code cannot be done inside a callback and need to be sync. * Do not forget to call {@link Acquired#unlock()} once you are done with it. * * @return an acquired object * @see #sync(Consumer) for auto-closeable capability */ default @NotNull Acquired lock() { return new Acquired<>(unwrap(), assignedThread()); } /** * Retrieves the acquirable value if and only if the element * is already present/ticked in the current thread. *

* Useful when you want only want to acquire an element when you are guaranteed * to do not create a huge performance impact. * * @return an optional containing the acquired element if safe, * {@link Optional#empty()} otherwise */ default @NotNull Optional local() { if (isLocal()) return Optional.of(unwrap()); return Optional.empty(); } /** * Gets if the acquirable element is local to this thread * * @return true if the element is linked to the current thread */ default boolean isLocal() { return Thread.currentThread() == assignedThread(); } /** * Locks the acquirable element, execute {@code consumer} synchronously and unlock the thread. *

* Free if the element is already present in the current thread, blocking otherwise. * * @param consumer the callback to execute once the element has been safely acquired * @see #async(Consumer) */ default void sync(@NotNull Consumer consumer) { Acquired acquired = lock(); try { consumer.accept(acquired.get()); } finally { acquired.unlock(); } } /** * Async version of {@link #sync(Consumer)}. * * @param consumer the callback to execute once the element has been safely acquired * @see #sync(Consumer) */ default void async(@NotNull Consumer consumer) { // TODO per-thread list AsyncUtils.runAsync(() -> sync(consumer)); } /** * Unwrap the contained object unsafely. *

* Should only be considered when thread-safety is not necessary (e.g. comparing positions) * * @return the unwrapped value */ @NotNull T unwrap(); @ApiStatus.Internal @NotNull TickThread assignedThread(); }