--- /dev/null 2015-01-12 18:08:49.000000000 +0100 +++ new/jdk/test/sun/misc/VM/GetNanoTimeAdjustment.java 2015-01-12 18:08:48.000000000 +0100 @@ -0,0 +1,249 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +import java.util.Objects; +import sun.misc.VM; + +/** + * @test + * @bug 8068730 + * @summary tests that VM.getgetNanoTimeAdjustment() works as expected. + * @run main GetNanoTimeAdjustment + * @author danielfuchs + */ +public class GetNanoTimeAdjustment { + + static final int MILLIS_IN_SECOND = 1000; + static final int NANOS_IN_MILLI = 1000_000; + static final int NANOS_IN_MICRO = 1000; + static final int NANOS_IN_SECOND = 1000_000_000; + + static final boolean verbose = true; + + static final class TestAssertException extends RuntimeException { + TestAssertException(String msg) { super(msg); } + } + + private static void assertEquals(long expected, long received, String msg) { + if (expected != received) { + throw new TestAssertException("Unexpected result for " + msg + + ".\n\texpected: " + expected + + "\n\tactual: " + received); + } else if (verbose) { + System.out.println("Got expected " + msg + ": " + received); + } + } + + private static void assertEquals(Object expected, Object received, String msg) { + if (!Objects.equals(expected, received)) { + throw new TestAssertException("Unexpected result for " + msg + + ".\n\texpected: " + expected + + "\n\tactual: " + received); + } else if (verbose) { + System.out.println("Got expected " + msg + ": " + received); + } + } + + static final long MAX_OFFSET = 0x0100000000L; + static final long MIN_OFFSET = -MAX_OFFSET; + static enum Answer { + YES, // isOffLimit = YES: we must get -1 + NO, // isOffLimit = NO: we must not not get -1 + MAYBE // isOffLimit = MAYBE: we might get -1 or a valid adjustment. + }; + static long distance(long one, long two) { + return one > two ? Math.subtractExact(one, two) + : Math.subtractExact(two, one); + } + + + static Answer isOffLimits(long before, long after, long offset) { + long relativeDistanceBefore = distance(before, offset); + long relativeDistanceAfter = distance(after, offset); + if (relativeDistanceBefore >= MAX_OFFSET && relativeDistanceAfter >= MAX_OFFSET) { + return Answer.YES; + } + if (relativeDistanceBefore < MAX_OFFSET && relativeDistanceAfter < MAX_OFFSET) { + if (relativeDistanceBefore == 0 || relativeDistanceAfter == 0) { + return Answer.MAYBE; // unlucky case where + } + return Answer.NO; + } + return Answer.MAYBE; + } + + static void testWithOffset(String name, long offset) { + System.out.println("Testing with offset: " + name); + long beforeMillis = System.currentTimeMillis(); + long adjustment = VM.getNanoTimeAdjustment(offset); + long afterMillis = System.currentTimeMillis(); + + if (offset >= beforeMillis/MILLIS_IN_SECOND + && offset <= afterMillis/MILLIS_IN_SECOND) { + if (adjustment == -1) { + // it's possible that we have fallen in the unlucky case + // where -1 was the genuine result. let's go backward a bit. + offset = offset - 10; + beforeMillis = System.currentTimeMillis(); + adjustment = VM.getNanoTimeAdjustment(offset); + afterMillis = System.currentTimeMillis(); + if (adjustment == -1) { + throw new RuntimeException(name + ": VM says " + offset + + " secs is too far off, " + + " when time in seconds is in [" + + beforeMillis/MILLIS_IN_SECOND + ", " + + afterMillis/MILLIS_IN_SECOND + + "]"); + } + } + } + + Answer isOffLimit = isOffLimits(beforeMillis/MILLIS_IN_SECOND, + afterMillis/MILLIS_IN_SECOND, offset); + switch (isOffLimit) { + case YES: + if (adjustment != -1) { + throw new RuntimeException(name + + ": VM should have returned -1 for " + + offset + + " when time in seconds is in [" + + beforeMillis/MILLIS_IN_SECOND + ", " + + afterMillis/MILLIS_IN_SECOND + "]"); + } + System.out.println("Got expected exception value: " + adjustment); + break; + case NO: + if (adjustment == -1) { + throw new RuntimeException(name + + "VM says " + offset + + " secs is too far off, " + + " when time in seconds is in [" + + beforeMillis/MILLIS_IN_SECOND + ", " + + afterMillis/MILLIS_IN_SECOND + + "]"); + } + break; + case MAYBE: + System.out.println("Adjustment: " + adjustment); + System.out.println("Can't assert for -1 with offset " + + offset + "(" + name + ")" + + " when time in seconds is in [" + + beforeMillis/MILLIS_IN_SECOND + ", " + + afterMillis/MILLIS_IN_SECOND + + "]"); + // not conclusive + } + + if (isOffLimit == Answer.NO || adjustment != -1) { + System.out.println("Validating adjustment: " + adjustment); + long expectedMax = distance(offset, beforeMillis/MILLIS_IN_SECOND) + * NANOS_IN_SECOND + + (beforeMillis % MILLIS_IN_SECOND) * NANOS_IN_MILLI + + (afterMillis - beforeMillis + 1) * NANOS_IN_MILLI; + long absoluteAdjustment = distance(0, adjustment); + if (absoluteAdjustment > expectedMax) { + long adjSec = absoluteAdjustment / NANOS_IN_SECOND; + long adjMil = (absoluteAdjustment % NANOS_IN_SECOND) / NANOS_IN_MILLI; + long adjMic = (absoluteAdjustment % NANOS_IN_MILLI) / NANOS_IN_MICRO; + long adjNan = (absoluteAdjustment % NANOS_IN_MICRO); + long expSec = expectedMax / NANOS_IN_SECOND; + long expMil = (expectedMax % NANOS_IN_SECOND) / NANOS_IN_MILLI; + long expMic = (expectedMax % NANOS_IN_MILLI) / NANOS_IN_MICRO; + long expNan = (expectedMax % NANOS_IN_MICRO); + System.err.println("Excessive adjustment: " + adjSec + "s, " + + adjMil + "ms, " + adjMic + "mics, " + adjNan + "ns"); + System.err.println("Epected max: " + expSec + "s, " + + expMil + "ms, " + expMic + "mics, " + expNan + "ns"); + + throw new RuntimeException(name + + ": Excessive adjustment: " + adjustment + + " when time in millis is in [" + + beforeMillis + ", " + afterMillis + + "] and offset in seconds is " + offset); + } + } + + } + + static void regular() { + System.out.println("*** Testing regular cases ***"); + final long start = System.currentTimeMillis(); + long offset = start/1000; + long adjustment = VM.getNanoTimeAdjustment(offset); + if (start != offset*1000) { + if (adjustment == -1) { + throw new RuntimeException("VM says " + offset + + " secs is too far off, but time millis is " + + System.currentTimeMillis()); + } + } + if (adjustment == -1) { + offset = System.currentTimeMillis()/1000 - 1024; + adjustment = VM.getNanoTimeAdjustment(offset); + if (adjustment == -1) { + throw new RuntimeException("VM says " + offset + + " secs is too far off, but time millis is " + + System.currentTimeMillis()); + } + } + if (adjustment > (start/1000 - offset + 20)*NANOS_IN_SECOND) { + throw new RuntimeException("Excessive adjustment: " + adjustment); + } + testWithOffset("System.currentTimeMillis()/1000", + System.currentTimeMillis()/1000); + testWithOffset("System.currentTimeMillis()/1000 - 1024", + System.currentTimeMillis()/1000 - 1024); + testWithOffset("System.currentTimeMillis()/1000 + 1024", + System.currentTimeMillis()/1000 + 1024); + } + + static void testLimits() { + System.out.println("*** Testing limits ***"); + testWithOffset("System.currentTimeMillis()/1000 - MAX_OFFSET + 1", + System.currentTimeMillis()/1000 - MAX_OFFSET + 1); + testWithOffset("System.currentTimeMillis()/1000 + MAX_OFFSET - 1", + System.currentTimeMillis()/1000 + MAX_OFFSET - 1); + testWithOffset("System.currentTimeMillis()/1000 - MAX_OFFSET", + System.currentTimeMillis()/1000 - MAX_OFFSET); + testWithOffset("System.currentTimeMillis()/1000 + MAX_OFFSET", + System.currentTimeMillis()/1000 + MAX_OFFSET); + testWithOffset("System.currentTimeMillis()/1000 - MAX_OFFSET - 1024", + System.currentTimeMillis()/1000 - MAX_OFFSET - 1024); + testWithOffset("System.currentTimeMillis()/1000 + MAX_OFFSET + 1024", + System.currentTimeMillis()/1000 + MAX_OFFSET + 1024); + testWithOffset("0", 0); + testWithOffset("-1", -1); + testWithOffset("Integer.MAX_VALUE + System.currentTimeMillis()/1000", + ((long)Integer.MAX_VALUE) + System.currentTimeMillis()/1000); + testWithOffset("System.currentTimeMillis()/1000 - Integer.MIN_VALUE", + System.currentTimeMillis()/1000 - Integer.MIN_VALUE); + testWithOffset("Long.MAX_VALUE", Long.MAX_VALUE); + testWithOffset("System.currentTimeMillis()/1000 - Long.MIN_VALUE", + (Long.MIN_VALUE + System.currentTimeMillis()/1000)*-1); + } + + public static void main(String[] args) throws Exception { + regular(); + testLimits(); + } + +}