HyCodeYourTale
classpublicabstractPriority 3

TickingThread

com.hypixel.hytale.server.core.util.thread.TickingThread

implements Runnable

12

Methods

12

Public Methods

7

Fields

3

Constructors

Constants

intNANOS_IN_ONE_MILLI= 1000000
intNANOS_IN_ONE_SECOND= 1000000000
intTPS= 30

Constructors

package-private
TickingThread()
public
TickingThread(String threadName)
public
TickingThread(String threadName, int tps, boolean daemon)

Methods

Public Methods (12)

public
void clearMetrics()
public
void debugAssertInTickingThread()
public
HistoricMetric getBufferedTickLengthMetricSet()
public
int getTickStepNanos()
public
int getTps()
public
boolean interrupt()
public
boolean isInThread()
public
boolean isStarted()
public
void run()
@Override
public
void setTps(int tps)
public
CompletableFuture<Void> start()
@Nonnull
public
void stop()

Fields

Public Fields (1)

publicstaticlong SLEEP_OFFSET

Private/Package Fields (6)

privateHistoricMetric bufferedTickLengthMetricSet
privateboolean daemon
privateThread thread
privateString threadName
privateint tickStepNanos
privateint tps

Inheritance

Parent
Current
Interface
Child

Use mouse wheel to zoom, drag to pan. Click nodes to navigate.

Related Classes

Source Code

package com.hypixel.hytale.server.core.util.thread;

import com.hypixel.hytale.logger.HytaleLogger;
import com.hypixel.hytale.metrics.metric.HistoricMetric;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;

public abstract class TickingThread implements Runnable {
   public static final int NANOS_IN_ONE_MILLI = 1000000;
   public static final int NANOS_IN_ONE_SECOND = 1000000000;
   public static final int TPS = 30;
   public static long SLEEP_OFFSET = 3000000L;
   private final String threadName;
   private final boolean daemon;
   private final AtomicBoolean needsShutdown = new AtomicBoolean(true);
   private int tps;
   private int tickStepNanos;
   private HistoricMetric bufferedTickLengthMetricSet;
   @Nullable
   private Thread thread;
   @Nonnull
   private CompletableFuture<Void> startedFuture = new CompletableFuture<>();

   public TickingThread(String threadName) {
      this(threadName, 30, false);
   }

   public TickingThread(String threadName, int tps, boolean daemon) {
      this.threadName = threadName;
      this.daemon = daemon;
      this.tps = tps;
      this.tickStepNanos = 1000000000 / tps;
      this.bufferedTickLengthMetricSet = HistoricMetric.builder((long)this.tickStepNanos, TimeUnit.NANOSECONDS)
         .addPeriod(10L, TimeUnit.SECONDS)
         .addPeriod(1L, TimeUnit.MINUTES)
         .addPeriod(5L, TimeUnit.MINUTES)
         .build();
   }

   @Override
   public void run() {
      try {
         this.onStart();
         this.startedFuture.complete(null);
         long beforeTick = System.nanoTime() - (long)this.tickStepNanos;

         while (this.thread != null && !this.thread.isInterrupted()) {
            long delta;
            if (!this.isIdle()) {
               while ((delta = System.nanoTime() - beforeTick) < (long)this.tickStepNanos) {
                  Thread.onSpinWait();
               }
            } else {
               delta = System.nanoTime() - beforeTick;
            }

            beforeTick = System.nanoTime();
            this.tick((float)delta / 1.0E9F);
            long tickLength = System.nanoTime() - beforeTick;
            this.bufferedTickLengthMetricSet.add(System.nanoTime(), tickLength);
            long sleepLength = (long)this.tickStepNanos - tickLength;
            if (!this.isIdle()) {
               sleepLength -= SLEEP_OFFSET;
            }

            if (sleepLength > 0L) {
               Thread.sleep(sleepLength / 1000000L);
            }
         }
      } catch (InterruptedException var9) {
         Thread.currentThread().interrupt();
      } catch (Throwable var10) {
         ((HytaleLogger.Api)HytaleLogger.getLogger().at(Level.SEVERE).withCause(var10)).log("Exception in thread %s:", this.thread);
      }

      if (this.needsShutdown.getAndSet(false)) {
         this.onShutdown();
      }
   }

   protected boolean isIdle() {
      return false;
   }

   protected abstract void tick(float var1);

   protected void onStart() {
   }

   protected abstract void onShutdown();

   @Nonnull
   public CompletableFuture<Void> start() {
      if (this.thread == null) {
         this.thread = new Thread(this, this.threadName);
         this.thread.setDaemon(this.daemon);
      } else if (this.thread.isAlive()) {
         throw new IllegalStateException("Thread '" + this.thread.getName() + "' is already started!");
      }

      this.thread.start();
      return this.startedFuture;
   }

   public boolean interrupt() {
      if (this.thread != null && this.thread.isAlive()) {
         this.thread.interrupt();
         return true;
      } else {
         return false;
      }
   }

   public void stop() {
      Thread thread = this.thread;
      if (thread != null) {
         try {
            int i = 0;

            while (thread.isAlive()) {
               thread.interrupt();
               thread.join((long)(this.tickStepNanos / 1000000));
               i += this.tickStepNanos / 1000000;
               if (i > 30000) {
                  StringBuilder sb = new StringBuilder();

                  for (StackTraceElement traceElement : thread.getStackTrace()) {
                     sb.append("\tat ").append(traceElement).append('\n');
                  }

                  HytaleLogger.getLogger().at(Level.SEVERE).log("Forcing TickingThread %s to stop:\n%s", thread, sb.toString());
                  thread.stop();
                  Thread var9 = null;
                  if (this.needsShutdown.getAndSet(false)) {
                     this.onShutdown();
                  }

                  return;
               }
            }

            Thread var10 = null;
         } catch (InterruptedException var8) {
            Thread.currentThread().interrupt();
         }
      }
   }

   public void setTps(int tps) {
      this.debugAssertInTickingThread();
      if (tps > 0 && tps <= 2048) {
         this.tps = tps;
         this.tickStepNanos = 1000000000 / tps;
         this.bufferedTickLengthMetricSet = HistoricMetric.builder((long)this.tickStepNanos, TimeUnit.NANOSECONDS)
            .addPeriod(10L, TimeUnit.SECONDS)
            .addPeriod(1L, TimeUnit.MINUTES)
            .addPeriod(5L, TimeUnit.MINUTES)
            .build();
      } else {
         throw new IllegalArgumentException("UpdatesPerSecond is out of bounds (<=0 or >2048): " + tps);
      }
   }

   public int getTps() {
      return this.tps;
   }

   public int getTickStepNanos() {
      return this.tickStepNanos;
   }

   public HistoricMetric getBufferedTickLengthMetricSet() {
      return this.bufferedTickLengthMetricSet;
   }

   public void clearMetrics() {
      this.bufferedTickLengthMetricSet.clear();
   }

   public void debugAssertInTickingThread() {
      if (!Thread.currentThread().equals(this.thread) && this.thread != null) {
         throw new AssertionError("Assert not in ticking thread!");
      }
   }

   public boolean isInThread() {
      return Thread.currentThread().equals(this.thread);
   }

   public boolean isStarted() {
      return this.thread != null && this.thread.isAlive() && this.needsShutdown.get();
   }

   @Deprecated
   protected void setThread(Thread thread) {
      this.thread = thread;
   }

   @Nullable
   protected Thread getThread() {
      return this.thread;
   }
}