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

com.fluxtion.agrona.concurrent.OffsetEpochNanoClock Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2014-2024 Real Logic Limited.
 *
 * 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
 *
 * https://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.fluxtion.agrona.concurrent;

import static java.util.concurrent.TimeUnit.HOURS;
import static java.util.concurrent.TimeUnit.MILLISECONDS;

/**
 * An accurate, zero-gc, pure-java, {@link EpochNanoClock} that calculates an initial epoch nano time based on
 * {@link System#currentTimeMillis()} and then uses that offset to adjust the return value of
 * {@link System#nanoTime()} to the UNIX epoch.
 * 

* The {@link #sample()} method can be used in order to reset these initial values if your system clock gets updated. *

* This class can be used from multiple threads safely. * * @see org.agrona.concurrent.SystemEpochNanoClock */ public class OffsetEpochNanoClock implements EpochNanoClock { private static final int DEFAULT_MAX_MEASUREMENT_RETRIES = 100; private static final long DEFAULT_MEASUREMENT_THRESHOLD_NS = 250; private static final long DEFAULT_RESAMPLE_INTERVAL_NS = HOURS.toNanos(1); private final int maxMeasurementRetries; private final long measurementThresholdNs; private final long resampleIntervalNs; private volatile TimeFields timeFields; /** * Constructs the clock with default configuration. */ public OffsetEpochNanoClock() { this(DEFAULT_MAX_MEASUREMENT_RETRIES, DEFAULT_MEASUREMENT_THRESHOLD_NS, DEFAULT_RESAMPLE_INTERVAL_NS); } /** * Constructs the clock with custom configuration parameters. * * @param maxMeasurementRetries the maximum number of times that this clock will attempt to re-sample the initial * time values. * @param measurementThresholdNs the desired accuracy window for the initial clock samples. * @param resampleIntervalNs the desired interval before the samples are automatically recalculated. The seed * recalculation enables the system to minimise clock drift if the system clock is * updated. */ @SuppressWarnings("this-escape") public OffsetEpochNanoClock( final int maxMeasurementRetries, final long measurementThresholdNs, final long resampleIntervalNs) { this.maxMeasurementRetries = maxMeasurementRetries; this.measurementThresholdNs = measurementThresholdNs; this.resampleIntervalNs = resampleIntervalNs; sample(); } /** * Explicitly resample the initial seeds. */ public void sample() { // Loop attempts to find a measurement that is accurate to a given threshold long bestInitialCurrentNanoTime = 0, bestInitialNanoTime = 0; long bestNanoTimeWindow = Long.MAX_VALUE; final int maxMeasurementRetries = this.maxMeasurementRetries; final long measurementThresholdNs = this.measurementThresholdNs; for (int i = 0; i < maxMeasurementRetries; i++) { final long firstNanoTime = System.nanoTime(); final long initialCurrentTimeMillis = System.currentTimeMillis(); final long secondNanoTime = System.nanoTime(); final long nanoTimeWindow = secondNanoTime - firstNanoTime; if (nanoTimeWindow < measurementThresholdNs) { timeFields = new TimeFields( MILLISECONDS.toNanos(initialCurrentTimeMillis), (firstNanoTime + secondNanoTime) >> 1, true); return; } else if (nanoTimeWindow < bestNanoTimeWindow) { bestInitialCurrentNanoTime = MILLISECONDS.toNanos(initialCurrentTimeMillis); bestInitialNanoTime = (firstNanoTime + secondNanoTime) >> 1; bestNanoTimeWindow = nanoTimeWindow; } } // If we never get a time below the threshold, pick the narrowest window we've seen so far. timeFields = new TimeFields( bestInitialCurrentNanoTime, bestInitialNanoTime, false); } /** * {@inheritDoc} */ public long nanoTime() { final TimeFields timeFields = this.timeFields; final long nanoTimeAdjustment = System.nanoTime() - timeFields.initialNanoTime; if (nanoTimeAdjustment < 0 || nanoTimeAdjustment > resampleIntervalNs) { sample(); return nanoTime(); } return timeFields.initialCurrentNanoTime + nanoTimeAdjustment; } /** * Gets whether the clock sampled the initial time offset accurately. * * @return true if the clock sampled the initial time offset accurately. */ public boolean isWithinThreshold() { return timeFields.isWithinThreshold; } static final class TimeFields { final long initialCurrentNanoTime; final long initialNanoTime; final boolean isWithinThreshold; private TimeFields( final long initialCurrentNanoTime, final long initialNanoTime, final boolean isWithinThreshold) { this.initialNanoTime = initialNanoTime; this.initialCurrentNanoTime = initialCurrentNanoTime; this.isWithinThreshold = isWithinThreshold; } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy