
com.yahoo.time.TimeBudget Maven / Gradle / Ivy
// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.time;
import com.yahoo.concurrent.UncheckedTimeoutException;
import java.time.Clock;
import java.time.Duration;
import java.time.Instant;
import java.util.Optional;
/**
* A TimeBudget can be used to track the time of an ongoing operation, possibly with a timeout.
*
* @author hakon
*/
public class TimeBudget {
private final Clock clock;
private final Instant start;
private final Optional timeout;
/** Returns a TimeBudget with a start time of now, and with the given timeout. */
public static TimeBudget fromNow(Clock clock, Duration timeout) {
return new TimeBudget(clock, clock.instant(), Optional.of(timeout));
}
public static TimeBudget from(Clock clock, Instant start, Optional timeout) {
return new TimeBudget(clock, start, timeout);
}
private TimeBudget(Clock clock, Instant start, Optional timeout) {
this.clock = clock;
this.start = start;
this.timeout = timeout.map(TimeBudget::makeNonNegative);
}
/** Returns time since start. */
public Duration timePassed() {
return nonNegativeBetween(start, clock.instant());
}
/** Returns the original timeout, if any. */
public Optional originalTimeout() {
return timeout;
}
/** Returns the deadline, if present. */
public Optional deadline() {
return timeout.map(start::plus);
}
/**
* Returns the time until deadline, if there is one.
*
* @return time until deadline. It's toMillis() is guaranteed to be positive.
* @throws UncheckedTimeoutException if the deadline has been reached or passed.
*/
public Optional timeLeftOrThrow() {
return timeout.map(timeout -> {
Duration passed = timePassed();
Duration left = timeout.minus(passed);
if (left.toMillis() <= 0) {
throw new UncheckedTimeoutException("Time since start " + passed + " exceeds timeout " + timeout);
}
return left;
});
}
/** Returns the time left, possibly negative if the deadline has passed. */
public Optional timeLeft() {
return timeout.map(timeout -> timeout.minus(timePassed()));
}
/** Returns the time left as a new TimeBudget. */
public TimeBudget timeLeftAsTimeBudget() {
Instant now = clock.instant();
return new TimeBudget(clock, now, deadline().map(d -> Duration.between(now, d)));
}
/** Returns a new TimeBudget with the same clock and start, but with this deadline. */
public TimeBudget withDeadline(Instant deadline) {
return new TimeBudget(clock, start, Optional.of(Duration.between(start, deadline)));
}
/** Returns a new TimeBudget with the given duration chopped off, reserved for something else. */
public TimeBudget withReserved(Duration chunk) {
return timeout.isEmpty() ? this : new TimeBudget(clock, start, Optional.of(timeout.get().minus(chunk)));
}
private static Duration nonNegativeBetween(Instant start, Instant end) {
return makeNonNegative(Duration.between(start, end));
}
private static Duration makeNonNegative(Duration duration) {
return duration.isNegative() ? Duration.ZERO : duration;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy