001package org.javasimon.clock;
002
003/**
004 * Utils related to timers and Clock class, especially default implementation based on system timers
005 * ({@link SimonClock#SYSTEM}).
006 *
007 * @since 3.5
008 */
009@SuppressWarnings({"UnusedDeclaration"})
010public class SimonClockUtils {
011
012        /**
013         * Value of {@link System#nanoTime()} at a particular time, when {@link #INIT_MILLIS} is initialized as well.
014         * Used in {@link #millisForNano(long)}.
015         *
016         * @since 3.3
017         */
018        public static final long INIT_NANOS = Calibration.initNanos;
019
020        /**
021         * Value of {@link System#currentTimeMillis()} at a particular time, when {@link #INIT_NANOS} is initialized as well.
022         * Used in {@link #millisForNano(long)}.
023         *
024         * @since 3.3
025         */
026        public static final long INIT_MILLIS = Calibration.initMillis;
027
028        /**
029         * Measured difference in {@link System#currentTimeMillis()} during calibration.
030         *
031         * @since 3.5
032         */
033        public static final long MILLIS_GRANULARITY = Calibration.millisGranularity;
034
035        /**
036         * Average difference in {@link System#nanoTime()} during calibration.
037         *
038         * @since 3.5
039         */
040        public static final long NANOS_GRANULARITY = Calibration.nanosGranularity;
041
042        /**
043         * Converts nano timer value into millis timestamp compatible with {@link System#currentTimeMillis()}. Method does not
044         * just divide nanos by one million, but also works with remembered values for milli- and nano-timers at one particular moment.
045         *
046         * @param nanos nano timer value
047         * @return ms timestamp
048         * @since 3.3 (moved from SimonManager where it was since 3.1)
049         */
050        public static long millisForNano(long nanos) {
051                return INIT_MILLIS + (nanos - INIT_NANOS) / SimonClock.NANOS_IN_MILLIS;
052        }
053
054        private static class Calibration {
055
056                private static final int NANO_CHANGES = 100;
057
058                private static long initNanos;
059                private static long initMillis;
060                private static long nanosGranularity;
061
062                private static long millisGranularity;
063
064                static {
065                        initMillis = System.currentTimeMillis();
066                        long oldNanos;
067                        while (true) {
068                                oldNanos = System.nanoTime();
069                                long nextMillis = System.currentTimeMillis();
070                                if (nextMillis > initMillis) {
071                                        millisGranularity = nextMillis - initMillis;
072                                        initMillis = nextMillis;
073                                        break;
074                                } else {
075                                        // this ensures that we should get the last possible nano value before initMillis
076                                        initNanos = oldNanos;
077                                }
078                        }
079
080                        long sumOfNanoDiffs = 0;
081                        int nanoChanges = 0;
082                        int nanoMeasurements = 0;
083                        // we will reuse oldNanos from before
084                        while (nanoChanges < NANO_CHANGES) {
085                                long nextNanos = System.nanoTime();
086                                nanoMeasurements++;
087                                if (nextNanos > oldNanos) {
088                                        nanoChanges++;
089                                        sumOfNanoDiffs += nextNanos - oldNanos;
090                                        oldNanos = nextNanos;
091                                }
092                        }
093                        nanosGranularity = sumOfNanoDiffs / nanoChanges;
094                        /*
095                        Produces funny results when repeated - granularity differences are striking even during a single Maven build:
096            nanosGranularity = 460 (based on 100 changes and 327 measurements)
097            nanosGranularity = 1198 (based on 100 changes and 324 measurements)
098            nanosGranularity = 605 (based on 100 changes and 328 measurements)
099                        System.out.println("nanosGranularity = " + nanosGranularity + " (based on " + nanoChanges + " changes and " + nanoMeasurements + " measurements)");
100                        System.out.println("millisGranularity = " + millisGranularity);
101                        */
102                }
103        }
104}