Author: jukka
Date: Thu Mar 13 14:32:48 2014
New Revision: 1577177

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

Increase accuracy of the accurate clock.
Introduce a configurable random noise element to the simple clock.
Remove the accidentally committed &~0xfL test modifier from 
System.currentTimeMillis()

Modified:
    
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/stats/Clock.java
    
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/stats/ClockTest.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=1577177&r1=1577176&r2=1577177&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
 Thu Mar 13 14:32:48 2014
@@ -17,6 +17,7 @@
 package org.apache.jackrabbit.oak.stats;
 
 import java.util.Date;
+import java.util.Random;
 import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.TimeUnit;
 
@@ -26,6 +27,15 @@ import java.util.concurrent.TimeUnit;
 public abstract class Clock {
 
     /**
+     * Maximum amount (in ms) of random noise to include in the time
+     * signal reported by the {@link #SIMPLE} clock. Configurable by the
+     * "simple.clock.noise" system property to make it easier to test
+     * the effect of an inaccurate system clock.
+     */
+    private static final int SIMPLE_CLOCK_NOISE =
+            Integer.getInteger("simple.clock.noise", 0);
+
+    /**
      * Millisecond granularity of the {@link #ACCURATE} clock.
      * Configurable by the "accurate.clock.granularity" system property
      * to make it easier to test the effect of a slow-moving clock on
@@ -123,12 +133,27 @@ public abstract class Clock {
      * Simple clock implementation based on {@link System#currentTimeMillis()},
      * which is known to be rather slow on some platforms.
      */
-    public static Clock SIMPLE = new Clock() {
-        @Override
-        public long getTime() {
-            return System.currentTimeMillis() & ~0xfL;
+    public static Clock SIMPLE = createSimpleClock();
+
+    private static Clock createSimpleClock() {
+        final int noise = SIMPLE_CLOCK_NOISE;
+        if (noise > 0) {
+            return new Clock() {
+                private final Random random = new Random();
+                @Override
+                public synchronized long getTime() {
+                    return System.currentTimeMillis() + random.nextInt(noise);
+                }
+            };
+        } else {
+            return new Clock() {
+                @Override
+                public long getTime() {
+                    return System.currentTimeMillis();
+                }
+            };
         }
-    };
+    }
 
     /**
      * Accurate clock implementation that uses interval timings from the
@@ -153,16 +178,24 @@ public abstract class Clock {
             if (now > ms + SYNC_INTERVAL) {
                 ms = SIMPLE.getTime();
                 ns = System.nanoTime();
+                // 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 = ms - now;
-                if (jump != 0 && Math.abs(jump) < SYNC_INTERVAL) {
-                    // currentTimeMillis() jumped a little bit, which was
-                    // probably caused by its lower granularity instead of
-                    // an adjustment to system time, so we reduce the jump
-                    // to just 0.5ms to make the reported time smoother
+                if (0 < jump && jump < 1000) {
+                    ms = now;
+                    ns -= 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 = now;
-                    ns += Long.signum(jump) * NS_IN_MS / 2;
-                } else {
-                    now = ms;
+                    ns += NS_IN_MS / 2;
                 }
             }
 

Modified: 
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/stats/ClockTest.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/stats/ClockTest.java?rev=1577177&r1=1577176&r2=1577177&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/stats/ClockTest.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/stats/ClockTest.java
 Thu Mar 13 14:32:48 2014
@@ -18,6 +18,8 @@ package org.apache.jackrabbit.oak.stats;
 
 import static junit.framework.Assert.assertTrue;
 
+import java.util.ArrayList;
+import java.util.List;
 import java.util.concurrent.Executors;
 import java.util.concurrent.ScheduledExecutorService;
 
@@ -25,6 +27,27 @@ import org.junit.Test;
 
 public class ClockTest {
 
+    /**
+     * Helper for checking how accurate the system clock is.
+     */
+    public static void main(String[] args) {
+        List<Long> values = new ArrayList<Long>();
+        long end = System.currentTimeMillis() + 10000; // 10 seconds
+        long last = System.currentTimeMillis();
+        long current = last;
+
+        do {
+            current = System.currentTimeMillis();
+            if (current != last && current != last + 1) {
+                values.add(current);
+            }
+            last = current;
+        } while (current < end);
+        for (int i = 1; i < values.size(); i++) {
+            System.out.println(values.get(i) + " " + (values.get(i) - 
values.get(i - 1)));
+        }
+    }
+
     @Test
     public void testClockDrift() throws InterruptedException {
         ScheduledExecutorService executor =


Reply via email to