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

com.mongodb.internal.time.TimePoint Maven / Gradle / Ivy

/*
 * Copyright 2008-present MongoDB, 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 com.mongodb.internal.time;

import com.mongodb.annotations.Immutable;
import com.mongodb.internal.VisibleForTesting;
import com.mongodb.internal.function.CheckedFunction;
import com.mongodb.internal.function.CheckedSupplier;
import com.mongodb.lang.Nullable;

import java.time.Clock;
import java.time.Duration;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

import static com.mongodb.assertions.Assertions.assertNotNull;
import static com.mongodb.internal.VisibleForTesting.AccessModifier.PRIVATE;
import static java.util.concurrent.TimeUnit.NANOSECONDS;

/**
 * A value-based class
 * representing a point on a timeline. The origin of this timeline (which is not
 * exposed) has no relation to the {@linkplain  Clock#systemUTC() system clock}.
 * The same timeline is used by all {@link TimePoint}s within the same process.
 * 

* Methods operating on a pair of {@link TimePoint}s, * for example, {@link #durationSince(TimePoint)}, {@link #compareTo(TimePoint)}, * or producing a point from another one, for example, {@link #add(Duration)}, * work correctly only if the duration between the points is not greater than * {@link Long#MAX_VALUE} nanoseconds, which is more than 292 years.

*

* This class is not part of the public API and may be removed or changed at any time.

*/ @Immutable class TimePoint implements Comparable, StartTime, Timeout { @Nullable private final Long nanos; TimePoint(@Nullable final Long nanos) { this.nanos = nanos; } @VisibleForTesting(otherwise = PRIVATE) static TimePoint at(@Nullable final Long nanos) { return new TimePoint(nanos); } @VisibleForTesting(otherwise = PRIVATE) long currentNanos() { return System.nanoTime(); } /** * Returns the current {@link TimePoint}. */ static TimePoint now() { return at(System.nanoTime()); } /** * Returns a {@link TimePoint} infinitely far in the future. */ static TimePoint infinite() { return at(null); } @Override public Timeout shortenBy(final long amount, final TimeUnit timeUnit) { if (isInfinite()) { return this; // shortening (lengthening) an infinite timeout does nothing } long durationNanos = NANOSECONDS.convert(amount, timeUnit); return TimePoint.at(assertNotNull(nanos) - durationNanos); } @Override public T checkedCall(final TimeUnit timeUnit, final CheckedSupplier onInfinite, final CheckedFunction onHasRemaining, final CheckedSupplier onExpired) throws E { if (this.isInfinite()) { return onInfinite.get(); } long remaining = remaining(timeUnit); if (remaining <= 0) { return onExpired.get(); } else { return onHasRemaining.apply(remaining); } } /** * @return true if this timepoint is infinite. */ private boolean isInfinite() { return nanos == null; } /** * @return this TimePoint, as a Timeout. Convenience for {@link StartTime} */ @Override public Timeout asTimeout() { return this; } /** * The number of whole time units that remain until this TimePoint * has expired. This should not be used to check for expiry, * but can be used to supply a remaining value, in the finest-grained * TimeUnit available, to some method that may time out. * This method must not be used with infinite TimePoints. * * @param unit the time unit * @return the remaining time * @throws AssertionError if the timeout is infinite. Always check if the * timeout {@link #isInfinite()} before calling. */ private long remaining(final TimeUnit unit) { if (isInfinite()) { throw new AssertionError("Infinite TimePoints have infinite remaining time"); } long remaining = assertNotNull(nanos) - currentNanos(); remaining = unit.convert(remaining, NANOSECONDS); return remaining <= 0 ? 0 : remaining; } /** * The {@link Duration} between {@link TimePoint#now()} and this {@link TimePoint}. * This method is functionally equivalent to {@code TimePoint.now().durationSince(this)}. * Note that the duration will represent fully-elapsed whole units. * * @throws AssertionError If this TimePoint is {@linkplain #isInfinite() infinite}. * @see #durationSince(TimePoint) */ public Duration elapsed() { if (isInfinite()) { throw new AssertionError("No time can elapse since an infinite TimePoint"); } return Duration.ofNanos(currentNanos() - assertNotNull(nanos)); } /** * The {@link Duration} between this {@link TimePoint} and {@code t}. * A {@linkplain Duration#isNegative() negative} {@link Duration} means that * this {@link TimePoint} is {@linkplain #compareTo(TimePoint) before} {@code t}. * * @see #elapsed() */ Duration durationSince(final TimePoint t) { if (this.isInfinite()) { throw new AssertionError("this timepoint is infinite, with no duration since"); } if (t.isInfinite()) { throw new AssertionError("the other timepoint is infinite, with no duration until"); } return Duration.ofNanos(nanos - assertNotNull(t.nanos)); } /** * @param timeoutValue value; if negative, the result is infinite * @param timeUnit timeUnit * @return a TimePoint that is the given number of timeUnits in the future */ @Override public TimePoint timeoutAfterOrInfiniteIfNegative(final long timeoutValue, final TimeUnit timeUnit) { if (timeoutValue < 0) { return infinite(); } return this.add(Duration.ofNanos(NANOSECONDS.convert(timeoutValue, timeUnit))); } /** * Returns a {@link TimePoint} that is {@code duration} away from this one. * * @param duration A duration that may also be {@linkplain Duration#isNegative() negative}. */ TimePoint add(final Duration duration) { if (isInfinite()) { throw new AssertionError("No time can be added to an infinite TimePoint"); } long durationNanos = duration.toNanos(); return TimePoint.at(assertNotNull(nanos) + durationNanos); } /** * If this {@link TimePoint} is less/greater than {@code t}, then it is before/after {@code t}. *

* {@inheritDoc}

*/ @Override public int compareTo(final TimePoint t) { if (Objects.equals(nanos, t.nanos)) { return 0; } else if (this.isInfinite()) { return 1; } else if (t.isInfinite()) { return -1; } return Long.signum(nanos - assertNotNull(t.nanos)); } @Override public boolean equals(final Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } final TimePoint timePoint = (TimePoint) o; return Objects.equals(nanos, timePoint.nanos); } @Override public int hashCode() { return Objects.hash(nanos); } @Override public String toString() { String remainingMs = isInfinite() ? "infinite" : "" + TimeUnit.MILLISECONDS.convert(currentNanos() - assertNotNull(nanos), NANOSECONDS); return "TimePoint{" + "nanos=" + nanos + "remainingMs=" + remainingMs + '}'; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy