All Downloads are FREE. Search and download functionalities are using the official Maven repository.

swim.concurrent.Clock Maven / Gradle / Ivy

There is a newer version: 4.3.15
Show newest version
// Copyright 2015-2019 SWIM.AI inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package swim.concurrent;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;

/**
 * Hashed wheel timer {@link Schedule}.
 */
public class Clock implements Schedule {
  /**
   * Immutable array of {@link #tickCount} timer buckets, each containing a
   * lock-free queue of timer events to execute for a particular modulus of
   * clock ticks.
   */
  final ClockQueue[] dial;

  /**
   * Barrier used to sequence clock startup.
   */
  final CountDownLatch startLatch;

  /**
   * Barrier used to sequence clock shutdown.
   */
  final CountDownLatch stopLatch;

  /**
   * Thread that executes timer events at their scheduled times.
   */
  final ClockThread thread;

  /**
   * Time at which the clock started, in nanoseconds, with arbitrary origin.
   * Set exactly once when the clock thread starts.
   */
  volatile long startTime;

  /**
   * Number of nanoseconds between successive clock ticks.
   */
  final long tickNanos;

  /**
   * Number of ticks per clock revolution.
   */
  final int tickCount;

  /**
   * Atomic bit field with {@link #STARTED} and {@link #STOPPED} flags.
   */
  volatile int status;

  /**
   * Constructs a new {@code Clock} with a timer resolution of {@code
   * tickMillis} milliseconds, and a clock period of {@code tickCount} ticks
   * per revolution.
   */
  public Clock(int tickMillis, int tickCount) {
    // Initialize the number of nanoseconds between clock ticks.
    if (tickMillis <= 0) {
      throw new IllegalArgumentException(Long.toString(tickMillis));
    }
    this.tickNanos = (long) tickMillis * 1000000L;

    // Initialize the number of ticks per clock revolution.
    if (tickCount <= 0) {
      throw new IllegalArgumentException(Integer.toString(tickCount));
    }
    // Round the tick count up to the next power of two.
    tickCount = tickCount - 1;
    tickCount |= tickCount >> 1;
    tickCount |= tickCount >> 2;
    tickCount |= tickCount >> 4;
    tickCount |= tickCount >> 8;
    tickCount |= tickCount >> 16;
    tickCount = tickCount + 1;
    this.tickCount = tickCount;

    // Initialize the clock dial with one revolution worth of clock ticks.
    this.dial = new ClockQueue[tickCount];
    for (int i = 0; i < tickCount; i += 1) {
      this.dial[i] = new ClockQueue((long) i);
    }

    // Initialize the barrier used to sequence clock startup.
    this.startLatch = new CountDownLatch(1);

    // Initialize the barrier used to sequence clock shutdown.
    this.stopLatch = new CountDownLatch(1);

    // Initialize--but don't start--the clock thread.
    this.thread = new ClockThread(this);
  }

  /**
   * Constructs a new {@code Clock} with the timer resolution and clock period
   * specified by the given {@code clockDef}.
   */
  public Clock(ClockDef clockDef) {
    this(clockDef.tickMillis, clockDef.tickCount);
  }

  /**
   * Constructs a new {@code Clock} with a timer resolution of {@link
   * #TICK_MILLIS} milliseconds, and a clock period of {@link #TICK_COUNT}
   * ticks per revolution.
   */
  public Clock() {
    this(TICK_MILLIS, TICK_COUNT);
  }

  /**
   * Returns the tick sequence number of the lowest clock tick that has yet to
   * finish executing.
   */
  public final long tick() {
    return this.thread.tick;
  }

  /**
   * Ensures that this {@code Clock} is up and running, starting up the clock
   * thread if it has not yet been started.
   *
   * @throws ScheduleException if this {@code Clock} has been stopped.
   */
  public final void start() {
    do {
      final int oldStatus = STATUS.get(this);
      if ((oldStatus & STOPPED) == 0) {
        // Clock hasn't yet stopped; make sure it has started.
        if ((oldStatus & STARTED) == 0) {
          final int newStatus = oldStatus | STARTED;
          // Try to set the STARTED flag; linearization point for clock startup.
          if (STATUS.compareAndSet(this, oldStatus, newStatus)) {
            // Initiate clock thread startup.
            willStart();
            this.thread.start();
            break;
          }
        } else {
          // Clock thread already started.
          break;
        }
      } else {
        throw new ScheduleException("Can't restart stopped clock");
      }
    } while (true);

    // Loop while the clock thread is not yet up and running.
    boolean interrupted = false;
    while (this.startLatch.getCount() != 0) {
      try {
        // Wait for clock thread startup to complete.
        this.startLatch.await();
      } catch (InterruptedException error) {
        interrupted = true;
      }
    }
    if (interrupted) {
      Thread.currentThread().interrupt();
    }
  }

  /**
   * Ensures that this {@code Clock} has been permanently stopped, shutting
   * down the clock thread, if it's currently running.  Upon return, this
   * {@code Clock} is guaranteed to be in the stopped state.
   */
  public final void stop() {
    boolean interrupted = false;
    do {
      final int oldStatus = STATUS.get(this);
      if ((oldStatus & STOPPED) == 0) {
        // Clock hasn't yet stopped; try to stop it.
        final int newStatus = oldStatus | STOPPED;
        // Try to set the STOPPED flag; linearization point for clock shutdown.
        if (STATUS.compareAndSet(this, oldStatus, newStatus)) {
          // Loop while the clock thread is still running.
          while (this.thread.isAlive()) {
            // Interrupt the clock thread so it will wakeup and die.
            this.thread.interrupt();
            try {
              // Wait for the clock thread to exit.
              this.thread.join(100);
            } catch (InterruptedException error) {
              interrupted = true;
            }
          }
        }
      } else {
        // Clock thread already stopped.
        break;
      }
    } while (true);

    // Loop while the clock thread is still running.
    while (this.stopLatch.getCount() != 0) {
      try {
        // Wait for clock thread shutdown to complete.
        this.stopLatch.await();
      } catch (InterruptedException e) {
        interrupted = true;
      }
    }
    if (interrupted) {
      Thread.currentThread().interrupt();
    }
  }

  @Override
  public TimerRef timer(TimerFunction timer) {
    // Ensure that the clock has started.
    start();

    // Create the context that binds the timer to this clock.
    final ClockTimer context = new ClockTimer(this, timer);
    if (timer instanceof Timer) {
      ((Timer) timer).setTimerContext(context);
    }

    // Return the timer context.
    return context;
  }

  @Override
  public TimerRef setTimer(long millis, TimerFunction timer) {
    if (millis < 0L) {
      throw new TimerException("negative timeout: " + Long.toString(millis));
    }

    // Ensure that the clock has started.
    start();

    // Create the context that binds the timer to this clock.
    final ClockTimer context = new ClockTimer(this, timer);
    if (timer instanceof Timer) {
      ((Timer) timer).setTimerContext(context);
    }

    // Schedule the timer for execution.
    schedule(millis, context);

    // Return the timer context.
    return context;
  }

  /**
   * Schedules a bound timer {@code context} for execution after {@code millis}
   * milliseconds has elapsed.
   */
  final void schedule(long millis, ClockTimer context) {
    // Invoke timer scheduling introspection callbacks.
    timerWillSchedule(context.timer, millis);
    if (context.timer instanceof Timer) {
      ((Timer) context.timer).timerWillSchedule(millis);
    }

    // Convert the timeout to nanoseconds.
    final long nanos = millis * 1000000L;
    // Compute the deadline for the timer in nanoseconds since clock start.
    final long deadline = Math.max(0L, nanoTime() + nanos - this.startTime);
    // Divide the deadline by the tick interval to get the tick sequence number
    // at which to fire the timer, rounding up to the next tick.
    long targetTick = (deadline + (this.tickNanos - 1L)) / this.tickNanos;
    // Take the modulus of the target tick with respect to to the number of
    // ticks per clock revolution, yielding the index in the dial at which to
    // insert the event.
    int targetHand = (int) (targetTick % (long) this.tickCount);

    // Create a timer event to insert into the clock.
    final ClockEvent newEvent = new ClockEvent(0L, targetTick, context, context.timer);
    // Atomically get the current timer event, and replace it with the
    // to-be-scheduled event; linearization point for timer un-cancellation.
    final ClockEvent oldEvent = ClockTimer.EVENT.getAndSet(context, newEvent);
    // Check if the timer had a previously scheduled event.
    if (oldEvent != null) {
      // Remove the timer from the previously scheduled event;
      // linearization point for timer cancellation.
      oldEvent.cancel();
    }

    // Get the event queue for the target hand of the clock.
    ClockQueue queue = this.dial[targetHand];
    // Capture the current foot of the queue.
    ClockEvent foot = queue.foot;
    // Search for the last event of in the queue, starting with foot.
    ClockEvent prev = foot;
    // Loop until the event is inserted into the first queue that will execute
    // after the timer deadline.
    do {
      // Load the next event after the currently referenced last event.
      final ClockEvent next = prev.next;
      if (next == null) {
        // prev is the last event in the queue.
        if (targetTick >= prev.insertTick) {
          // prev was inserted before the target tick, indicating that the
          // timer thread hasn't finished executing the target tick yet.
          // prev.insertTick is the next tick sequence number that the clock
          // thread will execute for the queue; set event.insertTick to match.
          newEvent.insertTick = prev.insertTick;
          // Try to insert the new event to the end of the queue;
          // linearization point for timer scheduling.
          if (ClockEvent.NEXT.compareAndSet(prev, null, newEvent)) {
            // Only update the foot reference if it lags at least two events
            // behind the last event in the queue.
            if (prev != foot) {
              // Try to update the foot reference; ok if this fails.
              ClockQueue.FOOT.compareAndSet(queue, foot, newEvent);
            }
            break;
          }
          // Lost insertion race to another thread; try again.
        } else {
          // The clock thread is currently executing, or has already executed,
          // the target tick; try the next hand of the clock.
          targetTick += 1L;
          targetHand = (int) (targetTick % (long) this.tickCount);
          queue = this.dial[targetHand];
          foot = queue.foot;
          prev = foot;
        }
      } else {
        // Jump to the new foot, if the previously loaded foot lags at least two
        // events behind the prev event; otherwise advance to the next event.
        final ClockEvent newFoot = queue.foot;
        if (foot != newFoot) {
          foot = newFoot;
          prev = foot;
        } else {
          prev = next;
        }
      }
    } while (true);
  }

  /**
   * Lifecycle callback invoked before the clock thread starts.
   */
  protected void willStart() {
    // stub
  }

  /**
   * Lifecycle callback invoked after the clock thread starts.
   */
  protected void didStart() {
    // stub
  }

  /**
   * Introspection callback invoked after each tick of the clock.  {@code tick}
   * is the sequence number of the tick that was executed; {@code waitedMillis}
   * is the number of milliseconds the clock thread slept before executing the
   * tick.  If {@code waitedMillis} is negative, then the clock thread didn't
   * start executing the tick until {@code -waitedMillis} milliseconds after
   * the scheduled tick deadline.
   */
  protected void didTick(long tick, long waitedMillis) {
    // stub
  }

  /**
   * Lifecycle callback invoked before the clock thread stops.
   */
  protected void willStop() {
    // stub
  }

  /**
   * Lifecycle callback invoked after the clock thread stops.
   */
  protected void didStop() {
    // stub
  }

  /**
   * Lifecycle callback invoked if the timer thread throws a fatal {@code
   * error}.  The clock thread will stop after invoking {@code didFail}.
   */
  protected void didFail(Throwable error) {
    error.printStackTrace();
  }

  /**
   * Introspection callback invoked before a {@code timer} is scheduled for
   * execution with a delay of {@code millis} milliseconds.
   */
  protected void timerWillSchedule(TimerFunction timer, long millis) {
    // stub
  }

  /**
   * Introspection callback invoked after a {@code timer} has been explicitly
   * cancelled; not invoked when a timer is implicitly cancelled, such as when
   * rescheduling an already scheduled timer.
   */
  protected void timerDidCancel(TimerFunction timer) {
    // stub
  }

  /**
   * Introspection callback invoked before a {@code timer} is executed.
   */
  protected void timerWillRun(TimerFunction timer) {
    // stub
  }

  /**
   * Invokes {@code timer.runTimer()}, or arranges for the asynchronous
   * execution of the provided {@code runnable}, which will itself invoke
   * {@code timer.runTimer()}.
   */
  protected void runTimer(TimerFunction timer, Runnable runnable) {
    timer.runTimer();
  }

  /**
   * Introspection callback invoked after a {@code timer} executes nominally.
   */
  protected void timerDidRun(TimerFunction timer) {
    // stub
  }

  /**
   * Introspection callback invoked after a {@code timer} execution fails by
   * throwing an {@code error}.
   */
  protected void timerDidFail(TimerFunction timer, Throwable error) {
    // stub
  }

  /**
   * Returns the current time, in nanoseconds, with arbitrary origin.
   * Used by the clock thread to determine the current time.  Defaults
   * to {@link System#nanoTime()}.  Can be overridden to substitute an
   * alternative time source.
   */
  protected long nanoTime() {
    return System.nanoTime();
  }

  /**
   * Parks the current thread for the specified number of {@code millis}.
   * Used by the clock thread to wait for the next clock tick.  Defaults
   * to {@link Thread#sleep(long)}.  Can be overridden to substitute an
   * alternative wait mechanism.
   */
  protected void sleep(long millis) throws InterruptedException {
    Thread.sleep(millis);
  }

  /**
   * Default number of milliseconds between clock ticks, used by the no-arg
   * {@link #Clock()} constructor.  Defaults to the value of the {@code
   * swim.clock.tick.millis} system property, if defined; otherwise defaults to
   * {@code 100} milliseconds.
   */
  public static final int TICK_MILLIS;

  /**
   * Default number of ticks per clock revolution, used by the no-arg {@link
   * #Clock()} constructor.  Defaults to the value of the {@code
   * swim.clock.tick.count} system property, if defined; otherwise defaults to
   * {@code 512} clock ticks per revolution.
   */
  public static final int TICK_COUNT;

  /**
   * Atomic {@link #status} bit flag indicating that the clock has started, and
   * is currently running.
   */
  static final int STARTED = 1 << 0;

  /**
   * Atomic {@link #status} bit flag indicating that the clock had previously
   * started, but is now permanently stopped.
   */
  static final int STOPPED = 1 << 1;

  /**
   * Atomic {@link #status} field updater, used to linearize clock startup and
   * shutdown.
   */
  static final AtomicIntegerFieldUpdater STATUS =
      AtomicIntegerFieldUpdater.newUpdater(Clock.class, "status");

  static {
    // Initializes the default number of milliseconds between clock ticks.
    int tickMillis;
    try {
      tickMillis = Integer.parseInt(System.getProperty("swim.clock.tick.millis"));
    } catch (NumberFormatException e) {
      tickMillis = 100;
    }
    TICK_MILLIS = tickMillis;

    // Initialize the default number of ticks per clock revolution.
    int tickCount;
    try {
      tickCount = Integer.parseInt(System.getProperty("swim.clock.tick.count"));
    } catch (NumberFormatException e) {
      tickCount = 512;
    }
    TICK_COUNT = tickCount;
  }
}

/**
 * Context that binds a {@code TimerFunction} to a {@code Clock}, and manages
 * the scheduling of at most one live {@code ClockEvent} at a time.
 */
final class ClockTimer implements TimerContext {
  /**
   * {@code Clock} to which the {@code timer} is bound.
   */
  final Clock clock;

  /**
   * {@code TimerFunction} to invoke when a scheduled event fires.
   */
  final TimerFunction timer;

  /**
   * Atomic reference to the currently scheduled event that will execute the
   * {@code timer} when fired; {@code null} when the {@code timer} is not
   * currently scheduled.
   */
  volatile ClockEvent event;

  /**
   * Constructs a new {@code ClockTimer} that binds the {@code timer} to the
   * {@code clock}.
   */
  ClockTimer(Clock clock, TimerFunction timer) {
    this.clock = clock;
    this.timer = timer;
  }

  @Override
  public Schedule schedule() {
    return this.clock;
  }

  @Override
  public boolean isScheduled() {
    final ClockEvent event = EVENT.get(this);
    return event != null && event.isScheduled();
  }

  @Override
  public void reschedule(long millis) {
    if (millis < 0L) {
      throw new TimerException("negative timeout: " + Long.toString(millis));
    }
    this.clock.schedule(millis, this);
  }

  @Override
  public boolean cancel() {
    // Atomically get the current timer event, and replace it with null.
    final ClockEvent event = EVENT.getAndSet(this, null);
    // Check if the timer has a previously scheduled event.
    if (event != null) {
      // Remove the timer from the previously scheduled timer event;
      // linearization point for timer cancellation.
      final TimerFunction timer = event.cancel();
      // Check if the timer event hadn't yet been fired or cancelled.
      if (timer != null) {
        // Invoke timer cancellation introspection callbacks.
        if (timer instanceof Timer) {
          ((Timer) timer).timerDidCancel();
        }
        clock.timerDidCancel(timer);
        return true;
      }
    }
    return false;
  }

  /**
   * Atomic {@link #event} field updater, used to linearize cancellation and
   * rescheduling of the {@code timer}.
   */
  static final AtomicReferenceFieldUpdater EVENT =
      AtomicReferenceFieldUpdater.newUpdater(ClockTimer.class, ClockEvent.class, "event");
}

/**
 * Lock-free, multiple publisher, single consumer queue of linked {@code
 * ClockEvent} items.
 */
final class ClockQueue {
  /**
   * First {@code ClockEvent} in the queue, from which all live events in the
   * queue are reachable; always non-{@code null}.  Only the clock thread is
   * permitted to advance the head of the queue.
   */
  volatile ClockEvent head;

  /**
   * Atomic reference from which the last {@code ClockEvent} in the queue can
   * be reached in constant time; always non-{@code null}.
   */
  volatile ClockEvent foot;

  /**
   * Constructs a new {@code ClockQueue} that will next execute the {@code
   * insertTick} sequence number.
   */
  ClockQueue(long insertTick) {
    this.head = new ClockEvent(insertTick, insertTick, null, null);
    this.foot = head;
  }

  /**
   * Atomic {@link #foot} field updater, used to optimize event insertion.
   */
  static final AtomicReferenceFieldUpdater FOOT =
      AtomicReferenceFieldUpdater.newUpdater(ClockQueue.class, ClockEvent.class, "foot");
}

/**
 * Linked {@code ClockQueue} item holding a {@code TimerFunction} to execute.
 */
final class ClockEvent implements Runnable {
  /**
   * Tick sequence number of the next tick that the clock thread will execute
   * at the time this {@code ClockEvent} was inserted into the queue.  {@code
   * insertTick} is non-decreasing over successive queued events, and the
   * {@code insertTick} of the last event in the queue always represents the
   * very next tick that the clock thread will execute.
   */
  long insertTick;

  /**
   * Tick sequence number during which to fire this event.
   */
  long targetTick;

  /**
   * {@code ClockTimer} on behalf of whom this {@code ClockEvent} is scheduled,
   * or {@code null} if this is a placeholder event.
   */
  final ClockTimer context;

  /**
   * Atomic reference to the {@code TimerFunction} to invoke when firing this
   * event; {@code null} when this event has been cancelled.
   */
  volatile TimerFunction timer;

  /**
   * Next {@code ClockEvent} in the linked queue; {@code null} if this is the
   * last event in the queue.
   */
  volatile ClockEvent next;

  /**
   * Constructs a new {@code ClockEvent} that will fire the {@code timer}
   * at the {@code targetTick} sequence number, and will be inserted into the
   * queue during the {@code insertTick} sequence number.
   */
  ClockEvent(long insertTick, long targetTick, ClockTimer context, TimerFunction timer) {
    this.insertTick = insertTick;
    this.targetTick = targetTick;
    this.context = context;
    this.timer = timer;
  }

  /**
   * Returns {@code true} if the {@link #timer} is non-{@code null}, indicating
   * that this {@code ClockEvent} is scheduled for execution.
   */
  boolean isScheduled() {
    return TIMER.get(this) != null;
  }

  /**
   * Atomically gets the scheduled {@link #timer}, and replaces it with {@code
   * null}, thereby preventing future execution, or concurrent cancellation.
   */
  TimerFunction cancel() {
    // Atomically get the scheduled timer, and replace it with null;
    // linearization point for timer execution and cancellation.
    return TIMER.getAndSet(this, null);
  }

  /**
   * Invokes the timer function of the associated timer context.
   */
  @Override
  public void run() {
    this.context.timer.runTimer();
  }

  /**
   * Atomic {@link #timer} field updater, used to linearize event cancellation.
   */
  static final AtomicReferenceFieldUpdater TIMER =
      AtomicReferenceFieldUpdater.newUpdater(ClockEvent.class, TimerFunction.class, "timer");

  /**
   * Atomic {@link #next} field updater, used to linearize event scheduling.
   */
  static final AtomicReferenceFieldUpdater NEXT =
      AtomicReferenceFieldUpdater.newUpdater(ClockEvent.class, ClockEvent.class, "next");
}

/**
 * Thread of execution that fires clock events at the appropriate times.
 */
final class ClockThread extends Thread {
  /**
   * {@code Clock} whose events this {@code ClockThread} fires.
   */
  final Clock clock;

  /**
   * Next tick sequence number that this {@code ClockThread} will execute.
   */
  long tick;

  /**
   * Constructs a new {@code ClockThread} that fires events for {@code clock}.
   */
  ClockThread(Clock clock) {
    setName("SwimClock" + THREAD_COUNT.getAndIncrement());
    setDaemon(true);
    this.clock = clock;
  }

  @Override
  public void run() {
    final Clock clock = this.clock;

    try {
      // Initialize the relative clock start time.
      long startTime = clock.nanoTime();
      if (startTime == 0L) {
        // Avoid clash with sentinel value that signifies an unstarted clock.
        startTime = 1L;
      }
      clock.startTime = startTime;

      // Linearization point for clock start.
      clock.startLatch.countDown();
      clock.didStart();

      // Loop while the clock has not been stopped.
      do {
        final long tick = this.tick;
        // Wait for the clock to reach the elapsed time of the next tick.
        final long waitedMillis = waitForTick(clock, tick);
        // Check if we had a nominal wakeup.
        if (waitedMillis != Long.MIN_VALUE) {
          // Execute the clock tick.
          executeTick(clock, tick);
          // Invoke the clock tick introspection callback, with a measure of the
          // clock latency.
          clock.didTick(tick, waitedMillis);
          // Increment the tick sequence number.
          this.tick = tick + 1L;
        }
      } while ((Clock.STATUS.get(clock) & Clock.STOPPED) == 0);

      clock.willStop();
    } catch (Throwable error) {
      if (Conts.isNonFatal(error)) {
        // Report internal clock error.
        clock.didFail(error);
      } else {
        // Rethrow fatal error.
        throw error;
      }
    } finally {
      // Force the clock into the stopped state.
      Clock.STATUS.set(clock, Clock.STOPPED);
      // Linearization point for clock stop.
      clock.stopLatch.countDown();
      clock.didStop();
    }
  }

  /**
   * Parks the clock thread until the {@code clock} time has reached the
   * elapsed time of the taregt {@code tick}.  Returns the total number of
   * milliseconds waited; the returned wait time can be negative if it's
   * taking longer than the clock's tick interval to execute timers.
   * Returns {@code Long.MIN_VALUE} if the clock was stopped while waiting
   * for the target {@code tick}.
   */
  static long waitForTick(final Clock clock, final long tick) {
    // The clock elapsed time of the target tick.
    final long deadline = clock.tickNanos * tick;
    // The clock elapsed time of the first wait for the target tick.
    final long initialTime = clock.nanoTime() - clock.startTime;
    // The current clock elapsed time.
    long currentTime = initialTime;
    do {
      // Check for elapsed time overflow.
      if (currentTime < 0L) {
        // Can't run for longer than about 292 years.
        throw new InternalError("Clock elapsed time overflow");
      }
      // Calculate the number of milliseconds until the deadline for the target
      // tick, rounding up to the next millisecond.
      final long sleepMillis = ((deadline - currentTime) + 999999L) / 1000000L;
      // Check if the deadline for the target tick is in the future.
      if (sleepMillis > 0L) {
        // Park the timer thread for the number of milliseconds until the
        // deadline for the target tick.
        try {
          clock.sleep(sleepMillis);
        } catch (InterruptedException e) {
          // Interrupted while waiting for the target tick; check if the clock
          // has been stopped.
          if ((Clock.STATUS.get(clock) & Clock.STOPPED) != 0) {
            // Return a sentinel value to signal that the clock stopped.
            return Long.MIN_VALUE;
          }
        }
        // Recompute the current clock elapsed time.
        currentTime = clock.nanoTime() - clock.startTime;
      } else {
        // We've reached the deadline for the target tick; recompute the
        // current clock elapsed time.
        currentTime = clock.nanoTime() - clock.startTime;
        // Compute the total number of milliseconds we waited.
        return (currentTime - initialTime) / 1000000L;
      }
    } while (true);
  }

  /**
   * Executes all {@code clock} timers set to fire at the target {@code tick}.
   */
  static void executeTick(final Clock clock, final long tick) {
    // Compute the tick sequence number for the next revolution of the clock.
    final long nextTick = tick + (long) clock.tickCount;
    // Compute the index in the clock dial of the target clock tick.
    final int hand = (int) (tick % (long) clock.tickCount);

    // Get the event queue for the target hand of the clock dial.
    final ClockQueue queue = clock.dial[hand];
    // The first known still scheduled event to keep in the queue.
    ClockEvent head = null;
    // The last known still scheduled event to keep in the queue.
    ClockEvent prev = null;
    // The next queued event to process.
    ClockEvent next = queue.head;
    // The sentinel event that will be inserted at the end of the queue to
    // complete the execution of this tick.
    final ClockEvent nextFoot = new ClockEvent(nextTick, nextTick, null, null);
    // Loop until no events scheduled for this tick remain in the queue.
    do {
      if (next.targetTick <= tick) {
        // The next event is scheduled for this tick; remove its timer.
        final TimerFunction timer = next.cancel();
        // Clear the event from the associated timer context.
        if (next.context != null) {
          ClockTimer.EVENT.compareAndSet(next.context, next, null);
        }
        if (timer != null) {
          // The timer wasn't cancelled; fire the event.
          try {
            clock.timerWillRun(timer);
            clock.runTimer(timer, next);
            clock.timerDidRun(timer);
          } catch (Throwable error) {
            if (Conts.isNonFatal(error)) {
              // The timer failed with a non-fatal error.
              clock.timerDidFail(timer, error);
            } else {
              // The timer failed with a fatal error.
              throw error;
            }
          }
        }
      } else if (next.isScheduled()) {
        // The next event is scheduled for a future revolution of the clock.
        if (prev != null) {
          // Insert the next event after the last kept event in the queue,
          // bypassing any fired or cancelled events.
          prev.next = next;
        } else {
          // The next event is the first event to keep in the queue.
          head = next;
        }
        // The next event is now the last known event to keep in the queue.
        prev = next;
      }
      // Check if the next event is the last in the queue.
      if (next.next == null) {
        // Try to finish tick execution by appending a cancelled event to the
        // end of the queue, whose insertTick is the next tick sequence number
        // that the clock thread will execute for this queue, preventing
        // further scheduling of events for the current tick.
        if (ClockEvent.NEXT.compareAndSet(next, null, nextFoot)) {
          // All events that will ever be scheduled for this tick have now been
          // cancelled or fired; update the foot of the queue to reference the
          // new foot event.
          ClockQueue.FOOT.set(queue, nextFoot);
          // Check if no events were kept in the queue.
          if (head == null) {
            // In which case the new foot is also the new head.
            head = nextFoot;
          }
          // Update the head of the queue.
          queue.head = head;
          break;
        }
      }
      // Advance to the next event in the queue.
      next = next.next;
    } while (true);
  }

  /**
   * Total number of clock threads that have ever been instantiated.  Used to
   * uniquely name clock threads.
   */
  static final AtomicInteger THREAD_COUNT = new AtomicInteger(0);
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy