/*
 * Copyright 2016 higherfrequencytrading.com
 *
 * 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 net.openhft.chronicle.core.util;

import org.jetbrains.annotations.NotNull;

import java.util.concurrent.locks.LockSupport;

/**
 * A timer for timeouts which is resilient to pauses in the JVM, tickTime can only increase if the JVM hasn't been paused.
 * <p>
 * For this to work, currentTimeMillis (or one of the methods that calls it) must be called more frequently than
 * every millisecond; the EventLoop implementations in chronicle-threads do this.
 * </p>
 */
public enum Time {
    ;

    static volatile long lastTime = System.currentTimeMillis();
    static volatile long tickTime = 0;

    public static long currentTimeMillis() {
        long now = System.currentTimeMillis();
        if (now == lastTime)
            return now;
        tickTime++;
        lastTime = now;
        return lastTime;
    }

    public static long tickTime() {
        currentTimeMillis();
        return tickTime;
    }

    public static void wait(@NotNull Object o, long waitTimeMS) throws InterruptedException, IllegalArgumentException {
        if ((int) waitTimeMS != waitTimeMS)
            throw new IllegalArgumentException("waitTimeMS: " + waitTimeMS);
        long end = tickTime() + waitTimeMS;
        for (long remaining; (remaining = end - tickTime()) > 0; )
            o.wait(remaining);
    }

    public static void parkNanos(long nanos) {
        long millis = nanos / 1000000;
        if (millis > 0) {
            long start = System.currentTimeMillis();
            long startTT = tickTime;
            LockSupport.parkNanos(nanos);
            long end = System.currentTimeMillis();
            long endTT = Math.min(end - start, millis) + startTT;
            if (endTT > tickTime)
                tickTime = endTT;
        } else {
            currentTimeMillis();
            LockSupport.parkNanos(nanos);
            currentTimeMillis();
        }
    }
}

@@ -650,51 +650,8 @@
 l;%0A%0A
-import org.jetbrains.annotations.NotNull;%0A%0A
 impo
@@ -693,16 +693,16 @@
 upport;%0A
+
 %0A/**%0A * 
@@ -1487,398 +1487,8 @@
  %7D%0A%0A
-    public static void wait(@NotNull Object o, long waitTimeMS) throws InterruptedException, IllegalArgumentException %7B%0A        if ((int) waitTimeMS != waitTimeMS)%0A            throw new IllegalArgumentException(%22waitTimeMS: %22 + waitTimeMS);%0A        long end = tickTime() + waitTimeMS;%0A        for (long remaining; (remaining = end - tickTime()) %3E 0; )%0A            o.wait(remaining);%0A    %7D%0A%0A
     
@@ -2029,32 +2029,32 @@
 ntTimeMillis();%0A
-
         %7D%0A    %7D%0A
@@ -2052,11 +2052,532 @@
  %7D%0A    %7D
+%0A%0A    /**%0A     * Sleeps in such a way that any long pause of the JVM, e.g. GC, will make it wait much longer to prevent timeouts being busted.%0A     *%0A     * @param sleepMS to pause for at least.%0A     * @throws IllegalArgumentException%0A     */%0A    public static void sleep(long sleepMS) %7B%0A        long sleepNS = sleepMS * 1_000_000;%0A        long pauseTimeNS = sleepNS / 16 + 1;%0A        int count = (int) (sleepNS / pauseTimeNS);%0A        for (int i = 0; i %3C count; i++)%0A            LockSupport.parkNanos(pauseTimeNS);%0A    %7D
 %0A%7D%0A

Add a pause tolerant sleep.