/* * Copyright (C) 2011-2014 Chris Vest (mr.chrisvest@gmail.com) * * 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 stormpot; import java.util.concurrent.TimeUnit; /** * A Timeout represents the maximum amount of time a caller is willing to wait * for a blocking operation to complete. * * Timeouts are independent of their units, so two timeouts of equivalent * duration but constructed in different units, will be equal to each other and * work exactly the same. * * Timeouts are also independent of "calendar time" in the sense that they * represent and work as a duration of absolute time. In other words, timeouts * do not grow or shrink with passing leap seconds or daylight savings time * adjustments. * * @author Chris Vest <mr.chrisvest@gmail.com> */ public final class Timeout { private final long timeout; private final TimeUnit unit; private final long timeoutBase; /** * Construct a new timeout with the given value and unit. The unit cannot be * `null`, but the timeout value is unrestricted. The meaning of a * negative timeout value is specific to the implementation of the use site, * but typically means that no amount of blocking or waiting is tolerated. * @param timeout A numerical value for the timeout. Can be zero or negative, * though the meaning is implementation specific. * @param unit The unit of the timeout value. Never `null`. */ public Timeout(long timeout, TimeUnit unit) { if (unit == null) { throw new IllegalArgumentException("The TimeUnit cannot be null"); } this.timeout = timeout; this.unit = unit; this.timeoutBase = getBaseUnit().convert(timeout, unit); } /** * Get the timeout value in terms of the {@link #getUnit() unit}. * @return A numerical value of the timeout. Possibly zero or negative. */ public long getTimeout() { return timeout; } /** * Get the unit for the {@link #getTimeout() timeout value}. * @return The {@link TimeUnit} of the timeout value. Never `null`. */ public TimeUnit getUnit() { return unit; } /** * Calculate a deadline, as an instant in the future, in terms of the * {@link #getBaseUnit() base unit}. Once you have a deadline, you can ask * how much time is left until it transpires, with the * {@link #getTimeLeft(long)} method, giving the deadline as an argument. * * If the {@link #getTimeout() timeout value} is really small, zero or * negative, then the deadline might be an instant in the past. * @return A numerical value that represents the deadline from "now" until * this timeout has passed. */ public long getDeadline() { return now() + timeoutBase; } /** * Get the timeout value in terms of the {@link #getBaseUnit() base unit}. * @return A numerical value of the timeout. Possibly zero or negative. */ public long getTimeoutInBaseUnit() { return timeoutBase; } /** * Calculate the amount of time that is left until the deadline, in terms * of the {@link #getBaseUnit() base unit}. The argument is a deadline value * that has been calculated with the {@link #getDeadline()} method. * @param deadline The reference deadline from {@link #getDeadline()}. * @return The amount of time, in terms of the * {@link #getBaseUnit() base unit}, that is left until the deadline. If * this value is negative, then the deadline has transpired. */ public long getTimeLeft(long deadline) { return deadline - now(); } /** * Get the unit of precision for the underlying clock, that is used by * {@link #getDeadline()} and {@link #getTimeLeft(long)}. * @return TimeUnit The unit of precision used by the clock in this Timeout. */ public TimeUnit getBaseUnit() { return TimeUnit.NANOSECONDS; } private long now() { return System.nanoTime(); } @Override public int hashCode() { return 31 * (1 + (int) (timeoutBase ^ (timeoutBase >>> 32))); } /** * Timeouts of equivalent duration are equal, even if they were constructed * with different units. * @return `true` if this Timeout value is equal to the given * Timeout value, `false` otherwise. */ @Override public boolean equals(Object obj) { if (!(obj instanceof Timeout)) { return false; } Timeout that = (Timeout) obj; return this.timeoutBase == that.timeoutBase; } }