Author: jukka
Date: Wed Mar 26 05:22:42 2014
New Revision: 1581695

URL: http://svn.apache.org/r1581695
Log:
OAK-1535: ClockTest.testClockDrift failures

Some further Clock.ACCURATE improvements

Modified:
    
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/stats/Clock.java

Modified: 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/stats/Clock.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/stats/Clock.java?rev=1581695&r1=1581694&r2=1581695&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/stats/Clock.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/stats/Clock.java
 Wed Mar 26 05:22:42 2014
@@ -188,7 +188,6 @@ public abstract class Clock {
      */
     public static Clock ACCURATE = new Clock() {
         private static final long NS_IN_MS = 1000000;
-        private static final long SYNC_INTERVAL = 1000; // ms
         private long ms = SIMPLE.getTime();
         private long ns = System.nanoTime();
         @Override
@@ -196,40 +195,67 @@ public abstract class Clock {
             long nowns = System.nanoTime();
             long nsIncrease = Math.max(nowns - ns, 0); // >= 0
 
-            long msIncrease = nsIncrease / NS_IN_MS;
+            long msIncrease = (nsIncrease + NS_IN_MS/2) / NS_IN_MS; // round up
             if (ACCURATE_CLOCK_GRANULARITY > 1) {
                 msIncrease -= msIncrease % ACCURATE_CLOCK_GRANULARITY;
             }
 
-            if (msIncrease < SYNC_INTERVAL) {
+            // If last clock sync was less than one second ago, the nanosecond
+            // timer drift will be insignificant and there's no need to 
re-sync.
+            if (msIncrease < 1000) {
                 return ms + msIncrease;
-            } else {
-                long nowms = SIMPLE.getTime();
-                // Check whether the system time jumped ahead or back
-                // from what we'd expect based on the nanosecond interval.
-                // If the jump was small, it was probably caused by low
-                // granularity of the system time. In that case we reduce
-                // the jump to just 0.5ms to smoothen the reported time.
-                // This should still keep clock drift in check as long as
-                // the nanosecond timings drift on average less than 0.5ms
-                // per second.
-                long jump = nowms - (ms + msIncrease);
-                if (0 < jump && jump < 1000) {
-                    ms += msIncrease;
-                    ns += msIncrease * NS_IN_MS - NS_IN_MS / 2;
-                } else if (0 > jump && jump > -1000) {
-                    // Note that the Math.max(..., 0) above will cause the
-                    // reported time to stay constant for a while instead
-                    // of going backwards because of this.
-                    ms += msIncrease;
-                    ns += msIncrease * NS_IN_MS + NS_IN_MS / 2;
-                } else {
-                    ms = nowms;
-                    ns = nowns;
+            }
+
+            // Last clock sync was up to ten seconds ago, so we synchronize
+            // smoothly to avoid both drift and sudden jumps.
+            long nowms = SIMPLE.getTime();
+            if (msIncrease < 10000) {
+                // 1) increase the ms and ns timestamps as if the estimated
+                //    ms increase was entirely correct
+                ms += msIncrease;
+                ns += msIncrease * NS_IN_MS;
+                // 2) compare the resulting time with the wall clock to see
+                //    if we're out of sync and to adjust accordingly
+                long jump = nowms - ms;
+                if (jump == 0) {
+                    // 2a) No deviation from wall clock.
+                    return ms;
+                } else if (0 < jump && jump < 100) {
+                    // 2b) The wall clock is up to 100ms ahead of us, probably
+                    // because of its low granularity. Adjust the ns timestamp
+                    // 0.1ms backward for future clock readings to jump that
+                    // much ahead to eventually catch up with the wall clock.
+                    ns -= NS_IN_MS / 10;
+                    return ms;
+                } else if (0 > jump && jump > -100) {
+                    // 2c) The wall clock is up to 100ms behind us, probably
+                    // because of its low granularity. Adjust the ns timestamp
+                    // 0.1ms forward for future clock readings to stay constant
+                    // (because of the Math.max(..., 0) above) for that long
+                    // to eventually catch up with the wall clock.
+                    ns += NS_IN_MS / 10;
+                    return ms;
                 }
-                return ms;
             }
+
+            // Last clock sync was over 10s ago or the nanosecond timer has
+            // drifted more than 100ms from the wall clock, so it's best to
+            // to a hard sync with no smoothing.
+            if (nowms >= ms + 1000) {
+                ms = nowms;
+                ns = nowns;
+            } else {
+                // Prevent the clock from moving backwards by setting the
+                // ms timestamp to exactly 1s ahead of the last sync time
+                // (to account for the time between clock syncs), and
+                // adjusting the ns timestamp ahead so that the reported time
+                // will stall until the clock would again move ahead.
+                ms = ms + 1000; // the 1s clock sync interval from above
+                ns = nowns + (ms - nowms) * NS_IN_MS;
+            }
+            return ms;
         }
+
         @Override
         public String toString() {
             return "Clock.ACCURATE";


Reply via email to