package net.minecraft.util.thread; import com.google.common.collect.ImmutableList; import com.mojang.logging.LogUtils; import it.unimi.dsi.fastutil.ints.Int2BooleanFunction; import java.util.List; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.Executor; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.atomic.AtomicInteger; import net.minecraft.Util; import net.minecraft.util.profiling.metrics.MetricCategory; import net.minecraft.util.profiling.metrics.MetricSampler; import net.minecraft.util.profiling.metrics.MetricsRegistry; import net.minecraft.util.profiling.metrics.ProfilerMeasured; import org.slf4j.Logger; public class ProcessorMailbox implements ProfilerMeasured, ProcessorHandle, AutoCloseable, Runnable { private static final Logger LOGGER = LogUtils.getLogger(); private static final int CLOSED_BIT = 1; private static final int SCHEDULED_BIT = 2; private final AtomicInteger status = new AtomicInteger(0); private final StrictQueue queue; private final Executor dispatcher; private final String name; public static ProcessorMailbox create(Executor dispatcher, String name) { return new ProcessorMailbox<>(new StrictQueue.QueueStrictQueue<>(new ConcurrentLinkedQueue()), dispatcher, name); } public ProcessorMailbox(StrictQueue queue, Executor dispatcher, String name) { this.dispatcher = dispatcher; this.queue = queue; this.name = name; MetricsRegistry.INSTANCE.add(this); } private boolean setAsScheduled() { int i; do { i = this.status.get(); if ((i & 3) != 0) { return false; } } while (!this.status.compareAndSet(i, i | 2)); return true; } private void setAsIdle() { int i; do { i = this.status.get(); } while (!this.status.compareAndSet(i, i & -3)); } private boolean canBeScheduled() { return (this.status.get() & 1) != 0 ? false : !this.queue.isEmpty(); } @Override public void close() { int i; do { i = this.status.get(); } while (!this.status.compareAndSet(i, i | 1)); } private boolean shouldProcess() { return (this.status.get() & 2) != 0; } private boolean pollTask() { if (!this.shouldProcess()) { return false; } else { Runnable runnable = this.queue.pop(); if (runnable == null) { return false; } else { Util.wrapThreadWithTaskName(this.name, runnable).run(); return true; } } } public void run() { try { this.pollUntil(i -> i == 0); } finally { this.setAsIdle(); this.registerForExecution(); } } public void runAll() { try { this.pollUntil(i -> true); } finally { this.setAsIdle(); this.registerForExecution(); } } @Override public void tell(T task) { this.queue.push(task); this.registerForExecution(); } private void registerForExecution() { if (this.canBeScheduled() && this.setAsScheduled()) { try { this.dispatcher.execute(this); } catch (RejectedExecutionException var4) { try { this.dispatcher.execute(this); } catch (RejectedExecutionException var3) { LOGGER.error("Cound not schedule mailbox", (Throwable)var3); } } } } private int pollUntil(Int2BooleanFunction continuePolling) { int i = 0; while (continuePolling.get(i) && this.pollTask()) { i++; } return i; } public int size() { return this.queue.size(); } public boolean hasWork() { return this.shouldProcess() && !this.queue.isEmpty(); } public String toString() { return this.name + " " + this.status.get() + " " + this.queue.isEmpty(); } @Override public String name() { return this.name; } @Override public List profiledMetrics() { return ImmutableList.of(MetricSampler.create(this.name + "-queue-size", MetricCategory.MAIL_BOXES, this::size)); } }