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

io.grpc.Deadline Maven / Gradle / Ivy

There is a newer version: 1.9.0
Show newest version
/*
 * Copyright 2016, Google Inc. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *    * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *    * Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following disclaimer
 * in the documentation and/or other materials provided with the
 * distribution.
 *
 *    * Neither the name of Google Inc. nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package io.grpc;

import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * An absolute deadline in system time.
 */
public final class Deadline implements Comparable {
  private static final SystemTicker SYSTEM_TICKER = new SystemTicker();
  // nanoTime has a range of just under 300 years. Only allow up to 100 years in the past or future
  // to prevent wraparound as long as process runs for less than ~100 years.
  private static final long MAX_OFFSET = TimeUnit.DAYS.toNanos(100 * 365);
  private static final long MIN_OFFSET = -MAX_OFFSET;

  /**
   * Create a deadline that will expire at the specified offset from the current system clock.
   * @param duration A non-negative duration.
   * @param units The time unit for the duration.
   * @return A new deadline.
   */
  public static Deadline after(long duration, TimeUnit units) {
    return after(duration, units, SYSTEM_TICKER);
  }

  // For testing
  static Deadline after(long duration, TimeUnit units, Ticker ticker) {
    checkNotNull(units, "units");
    return new Deadline(ticker, units.toNanos(duration), true);
  }

  private final Ticker ticker;
  private final long deadlineNanos;
  private volatile boolean expired;

  private Deadline(Ticker ticker, long offset, boolean baseInstantAlreadyExpired) {
    this(ticker, ticker.read(), offset, baseInstantAlreadyExpired);
  }

  private Deadline(Ticker ticker, long baseInstant, long offset,
      boolean baseInstantAlreadyExpired) {
    this.ticker = ticker;
    // Clamp to range [MIN_OFFSET, MAX_OFFSET]
    offset = Math.min(MAX_OFFSET, Math.max(MIN_OFFSET, offset));
    deadlineNanos = baseInstant + offset;
    expired = baseInstantAlreadyExpired && offset <= 0;
  }

  /**
   * Has this deadline expired
   * @return {@code true} if it has, otherwise {@code false}.
   */
  public boolean isExpired() {
    if (!expired) {
      if (deadlineNanos - ticker.read() <= 0) {
        expired = true;
      } else {
        return false;
      }
    }
    return true;
  }

  /**
   * Is {@code this} deadline before another.
   */
  public boolean isBefore(Deadline other) {
    return this.deadlineNanos - other.deadlineNanos < 0;
  }

  /**
   * Return the minimum deadline of {@code this} or an other deadline.
   * @param other deadline to compare with {@code this}.
   */
  public Deadline minimum(Deadline other) {
    return isBefore(other) ? this : other;
  }

  /**
   * Create a new deadline that is offset from {@code this}.
   */
  // TODO(ejona): This method can cause deadlines to grow too far apart. For example:
  // Deadline.after(100 * 365, DAYS).offset(100 * 365, DAYS) would be less than
  // Deadline.after(-100 * 365, DAYS)
  public Deadline offset(long offset, TimeUnit units) {
    // May already be expired
    if (offset == 0) {
      return this;
    }
    return new Deadline(ticker, deadlineNanos, units.toNanos(offset), isExpired());
  }

  /**
   * How much time is remaining in the specified time unit. Internal units are maintained as
   * nanoseconds and conversions are subject to the constraints documented for
   * {@link TimeUnit#convert}. If there is no time remaining, the returned duration is how
   * long ago the deadline expired.
   */
  public long timeRemaining(TimeUnit unit) {
    final long nowNanos = ticker.read();
    if (!expired && deadlineNanos - nowNanos <= 0) {
      expired = true;
    }
    return unit.convert(deadlineNanos - nowNanos, TimeUnit.NANOSECONDS);
  }

  /**
   * Schedule a task to be run when the deadline expires.
   * @param task to run on expiration
   * @param scheduler used to execute the task
   * @return {@link ScheduledFuture} which can be used to cancel execution of the task
   */
  public ScheduledFuture runOnExpiration(Runnable task, ScheduledExecutorService scheduler) {
    checkNotNull(task, "task");
    checkNotNull(scheduler, "scheduler");
    return scheduler.schedule(new LogExceptionRunnable(task),
        deadlineNanos - ticker.read(), TimeUnit.NANOSECONDS);
  }

  @Override
  public String toString() {
    return timeRemaining(TimeUnit.NANOSECONDS) + " ns from now";
  }

  @Override
  public int compareTo(Deadline that) {
    long diff = this.deadlineNanos - that.deadlineNanos;
    if (diff < 0) {
      return -1;
    } else if (diff > 0) {
      return 1;
    }
    return 0;
  }

  /** Time source representing nanoseconds since fixed but arbitrary point in time. */
  abstract static class Ticker {
    /** Returns the number of nanoseconds since this source's epoch. */
    public abstract long read();
  }

  private static class SystemTicker extends Ticker {
    @Override
    public long read() {
      return System.nanoTime();
    }
  }

  private static  T checkNotNull(T reference, Object errorMessage) {
    if (reference == null) {
      throw new NullPointerException(String.valueOf(errorMessage));
    }
    return reference;
  }

  private static class LogExceptionRunnable implements Runnable {
    private static final Logger log = Logger.getLogger(LogExceptionRunnable.class.getName());

    private final Runnable task;

    public LogExceptionRunnable(Runnable task) {
      this.task = checkNotNull(task, "task");
    }

    @Override
    public void run() {
      try {
        task.run();
      } catch (Throwable t) {
        log.log(Level.SEVERE, "Exception while executing runnable " + task, t);
        if (t instanceof RuntimeException) {
          throw (RuntimeException) t;
        } else if (t instanceof Error) {
          throw (Error) t;
        } else {
          throw new RuntimeException(t);
        }
      }
    }

    @Override
    public String toString() {
      return "Deadline.LogExceptionRunnable(" + task + ")";
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy