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

org.apache.solr.common.util.TimeSource Maven / Gradle / Ivy

There is a newer version: 9.8.1
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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 org.apache.solr.common.util;

import java.lang.invoke.MethodHandles;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Source of time.
 * 

NOTE: depending on implementation returned values may not be related in any way to the * current Epoch or calendar time, and they may even be negative - but the API guarantees that * they are always monotonically increasing.

*/ public abstract class TimeSource { private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); /** * Implementation that uses {@link System#currentTimeMillis()}. * This implementation's {@link #getTimeNs()} returns the same values as * {@link #getEpochTimeNs()}. */ public static final class CurrentTimeSource extends TimeSource { @Override @SuppressForbidden(reason = "Needed to provide timestamps based on currentTimeMillis.") public long getTimeNs() { return TimeUnit.NANOSECONDS.convert(System.currentTimeMillis(), TimeUnit.MILLISECONDS); } @Override public long getEpochTimeNs() { return getTimeNs(); } @Override public long[] getTimeAndEpochNs() { long time = getTimeNs(); return new long[] {time, time}; } @Override public void sleep(long ms) throws InterruptedException { Thread.sleep(ms); } @Override public long convertDelay(TimeUnit fromUnit, long value, TimeUnit toUnit) { return toUnit.convert(value, fromUnit); } } /** * Implementation that uses {@link System#nanoTime()}. * Epoch time is initialized using {@link CurrentTimeSource}, and then * calculated as the elapsed number of nanoseconds as measured by this * implementation. */ public static final class NanoTimeSource extends TimeSource { private final long epochStart; private final long nanoStart; public NanoTimeSource() { epochStart = CURRENT_TIME.getTimeNs(); nanoStart = System.nanoTime(); } @Override public long getTimeNs() { return System.nanoTime(); } @Override public long getEpochTimeNs() { return epochStart + getTimeNs() - nanoStart; } @Override public long[] getTimeAndEpochNs() { long time = getTimeNs(); return new long[] {time, epochStart + time - nanoStart}; } @Override public void sleep(long ms) throws InterruptedException { Thread.sleep(ms); } @Override public long convertDelay(TimeUnit fromUnit, long value, TimeUnit toUnit) { return toUnit.convert(value, fromUnit); } } /** Implementation that uses {@link #NANO_TIME} accelerated by a double multiplier. */ public static final class SimTimeSource extends TimeSource { final double multiplier; final long nanoStart; final long epochStart; /** * Create a simulated time source that runs faster than real time by a multiplier. * @param multiplier must be greater than 0.0 */ public SimTimeSource(double multiplier) { this.multiplier = multiplier; epochStart = CURRENT_TIME.getTimeNs(); nanoStart = NANO_TIME.getTimeNs(); } @Override public long getTimeNs() { return nanoStart + Math.round((double)(NANO_TIME.getTimeNs() - nanoStart) * multiplier); } @Override public long getEpochTimeNs() { return epochStart + getTimeNs() - nanoStart; } @Override public long[] getTimeAndEpochNs() { long time = getTimeNs(); return new long[] {time, epochStart + time - nanoStart}; } @Override public void sleep(long ms) throws InterruptedException { ms = Math.round((double)ms / multiplier); Thread.sleep(ms); } @Override public long convertDelay(TimeUnit fromUnit, long value, TimeUnit toUnit) { long nano = Math.round((double)TimeUnit.NANOSECONDS.convert(value, fromUnit) / multiplier); return toUnit.convert(nano, TimeUnit.NANOSECONDS); } @Override public String toString() { return super.toString() + ":" + multiplier; } } /** This instance uses {@link CurrentTimeSource} for generating timestamps. */ public static final TimeSource CURRENT_TIME = new CurrentTimeSource(); /** This instance uses {@link NanoTimeSource} for generating timestamps. */ public static final TimeSource NANO_TIME = new NanoTimeSource(); private static Map simTimeSources = new ConcurrentHashMap<>(); /** * Obtain an instance of time source. * @param type supported types: currentTime, nanoTime and accelerated * time with a double factor in the form of simTime:FACTOR, eg. * simTime:2.5 * @return one of the supported types */ public static TimeSource get(String type) { if (type == null) { return NANO_TIME; } else if (type.equals("currentTime") || type.equals(CurrentTimeSource.class.getSimpleName())) { return CURRENT_TIME; } else if (type.equals("nanoTime") || type.equals(NanoTimeSource.class.getSimpleName())) { return NANO_TIME; } else if (type.startsWith("simTime") || type.startsWith(SimTimeSource.class.getSimpleName())) { return simTimeSources.computeIfAbsent(type, t -> { String[] parts = t.split(":"); double mul = 1.0; if (parts.length != 2) { log.warn("Invalid simTime specification, assuming multiplier==1.0: '{}'.", type); } else { try { mul = Double.parseDouble(parts[1]); } catch (Exception e) { log.warn("Invalid simTime specification, assuming multiplier==1.0: '{}'.", type); } } return new SimTimeSource(mul); }); } else { throw new UnsupportedOperationException("Unsupported time source type '" + type + "'."); } } /** * Return a time value, in nanosecond units. Depending on implementation this value may or * may not be related to Epoch time. */ public abstract long getTimeNs(); /** * Return Epoch time. Implementations that are not natively based on epoch time may * return values that are consistently off by a (small) fixed number of milliseconds from * the actual epoch time. */ public abstract long getEpochTimeNs(); /** * Return both the source's time value and the corresponding epoch time * value. This method ensures that epoch time calculations use the same internal * value of time as that reported by {@link #getTimeNs()}. * @return an array where the first element is {@link #getTimeNs()} and the * second element is {@link #getEpochTimeNs()}. */ public abstract long[] getTimeAndEpochNs(); /** * Sleep according to this source's notion of time. Eg. accelerated time source such as * {@link SimTimeSource} will sleep proportionally shorter, according to its multiplier. * @param ms number of milliseconds to sleep * @throws InterruptedException when the current thread is interrupted */ public abstract void sleep(long ms) throws InterruptedException; /** * This method allows using TimeSource with APIs that require providing just plain time intervals, * eg. {@link Object#wait(long)}. Values returned by this method are adjusted according to the * time source's notion of time - eg. accelerated time source provided by {@link SimTimeSource} * will return intervals that are proportionally shortened by the multiplier. *

NOTE: converting small values may significantly affect precision of the returned values * due to rounding, especially for accelerated time source, so care should be taken to use time * units that result in relatively large values. For example, converting a value of 1 expressed * in seconds would result in less precision than converting a value of 1000 expressed in milliseconds.

* @param fromUnit source unit * @param value original value * @param toUnit target unit * @return converted value, possibly scaled by the source's notion of accelerated time * (see {@link SimTimeSource}) */ public abstract long convertDelay(TimeUnit fromUnit, long value, TimeUnit toUnit); public String toString() { return getClass().getSimpleName(); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy