From 8e93b53e3e5994fb42f938a0f485eaec087c37c5 Mon Sep 17 00:00:00 2001 From: Katja Kantserova Date: Wed, 21 Jan 2015 15:06:31 +0100 Subject: [PATCH 1/7] 8069296: java/lang/management/MemoryMXBean/LowMemoryTest.java should be quarantined Reviewed-by: sla --- jdk/test/ProblemList.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/jdk/test/ProblemList.txt b/jdk/test/ProblemList.txt index c6c35f2e4d6..4954f772f8a 100644 --- a/jdk/test/ProblemList.txt +++ b/jdk/test/ProblemList.txt @@ -123,7 +123,6 @@ # 8029891 java/lang/ClassLoader/deadlock/GetResource.java generic-all - ############################################################################ # jdk_instrument @@ -142,6 +141,9 @@ java/lang/instrument/RetransformBigClass.sh generic-all # 8058492 java/lang/management/ThreadMXBean/FindDeadlocks.java generic-all +# 8069286 +java/lang/management/MemoryMXBean/LowMemoryTest.java generic-all + ############################################################################ # jdk_jmx From 167de7ae02ce4e5338b8d1d5efc4c852ed7d61d6 Mon Sep 17 00:00:00 2001 From: Alexander Kulyakthin Date: Fri, 23 Jan 2015 14:20:52 +0100 Subject: [PATCH 2/7] 8067945: SVC jdk/test/* should be cleaned from JRE layout dependency Reviewed-by: sla --- jdk/test/com/sun/jdi/ImmutableResourceTest.sh | 2 +- jdk/test/com/sun/jdi/JITDebug.sh | 6 +++--- .../sun/jdi/connect/spi/JdiLoadedByCustomLoader.java | 11 +++-------- .../sun/jdi/connect/spi/JdiLoadedByCustomLoader.sh | 2 +- .../HotSpotDiagnosticMXBean/CheckOrigin.java | 6 +----- jdk/test/com/sun/tools/attach/BasicTests.java | 3 +-- jdk/test/com/sun/tools/attach/PermissionTest.java | 4 +--- jdk/test/com/sun/tools/attach/ProviderTest.java | 5 ++--- jdk/test/com/sun/tools/attach/TempDirTest.java | 4 +--- .../jmxremote/bootstrap/CustomLauncherTest.java | 8 +------- .../jmxremote/bootstrap/LocalManagementTest.java | 8 +------- 11 files changed, 16 insertions(+), 43 deletions(-) diff --git a/jdk/test/com/sun/jdi/ImmutableResourceTest.sh b/jdk/test/com/sun/jdi/ImmutableResourceTest.sh index 29c72ed6254..95bc8778b6e 100644 --- a/jdk/test/com/sun/jdi/ImmutableResourceTest.sh +++ b/jdk/test/com/sun/jdi/ImmutableResourceTest.sh @@ -92,7 +92,7 @@ fi # echo "JDK under test is: $TESTJAVA" # -CP="-classpath ${TESTCLASSES}${PATHSEP}${TESTJAVA}/lib/tools.jar" +CP="-classpath ${TESTCLASSES}" # Compile the test class using the classpath we need: # env diff --git a/jdk/test/com/sun/jdi/JITDebug.sh b/jdk/test/com/sun/jdi/JITDebug.sh index c647e1153b5..423fe26521a 100644 --- a/jdk/test/com/sun/jdi/JITDebug.sh +++ b/jdk/test/com/sun/jdi/JITDebug.sh @@ -103,15 +103,15 @@ if [ -z "${TESTJAVA}" ] ; then #if running standalone (no test harness of any kind), compile the #support files and the test case ${TESTJAVA}/bin/javac -d ${TESTCLASSES} \ - -classpath "$TESTJAVA/lib/tools.jar${PATHSEP}${TESTSRC}" \ + -classpath "${TESTSRC}" \ TestScaffold.java VMConnection.java TargetListener.java TargetAdapter.java ${TESTJAVA}/bin/javac -d ${TESTCLASSES} \ - -classpath "$TESTJAVA/lib/tools.jar${PATHSEP}${TESTSRC}" -g \ + -classpath "${TESTSRC}" -g \ JITDebug.java fi echo "JDK under test is: $TESTJAVA" # -CLASSPATH="$TESTJAVA/lib/tools.jar${PATHSEP}${TESTCLASSES}" +CLASSPATH="${TESTCLASSES}" export CLASSPATH CP="-classpath \"${CLASSPATH}\"" # diff --git a/jdk/test/com/sun/jdi/connect/spi/JdiLoadedByCustomLoader.java b/jdk/test/com/sun/jdi/connect/spi/JdiLoadedByCustomLoader.java index c01f54a79f4..11a522c727f 100644 --- a/jdk/test/com/sun/jdi/connect/spi/JdiLoadedByCustomLoader.java +++ b/jdk/test/com/sun/jdi/connect/spi/JdiLoadedByCustomLoader.java @@ -22,9 +22,8 @@ */ /* - * Creates a URLClassLoader from 2 file URLs. The first - * file URL is constructed from the given argument. The - * second is the SDK tools.jar. Once created the test + * Creates a URLClassLoader from a file URL. The file URL + * is constructed from the given argument. Once created the test * attempts to load another test case (ListConnectors) * using the class loader and then it invokes the list() * method. @@ -39,13 +38,9 @@ public class JdiLoadedByCustomLoader { public static void main(String args[]) throws Exception { // create files from given arguments and tools.jar File f1 = new File(args[0]); - String home = System.getProperty("java.home"); - String tools = ".." + File.separatorChar + "lib" + - File.separatorChar + "tools.jar"; - File f2 = (new File(home, tools)).getCanonicalFile(); // create class loader - URL[] urls = { f1.toURL(), f2.toURL() }; + URL[] urls = { f1.toURL() }; URLClassLoader cl = new URLClassLoader(urls); // load ListConnectors using the class loader diff --git a/jdk/test/com/sun/jdi/connect/spi/JdiLoadedByCustomLoader.sh b/jdk/test/com/sun/jdi/connect/spi/JdiLoadedByCustomLoader.sh index 2b104822b17..d508c00c727 100644 --- a/jdk/test/com/sun/jdi/connect/spi/JdiLoadedByCustomLoader.sh +++ b/jdk/test/com/sun/jdi/connect/spi/JdiLoadedByCustomLoader.sh @@ -68,7 +68,7 @@ SOMEOTHERDIR="${TESTCLASSES}"/someotherdir $JAVAC -d "${TESTCLASSES}" "${TESTSRC}"/JdiLoadedByCustomLoader.java mkdir "${SOMEOTHERDIR}" -$JAVAC -d "${SOMEOTHERDIR}" -classpath "${TESTSRC}${PS}${TESTJAVA}/lib/tools.jar" \ +$JAVAC -d "${SOMEOTHERDIR}" -classpath "${TESTSRC}" \ "${TESTSRC}"/ListConnectors.java # Run the test diff --git a/jdk/test/com/sun/management/HotSpotDiagnosticMXBean/CheckOrigin.java b/jdk/test/com/sun/management/HotSpotDiagnosticMXBean/CheckOrigin.java index 378310cf56a..27d80ffa106 100644 --- a/jdk/test/com/sun/management/HotSpotDiagnosticMXBean/CheckOrigin.java +++ b/jdk/test/com/sun/management/HotSpotDiagnosticMXBean/CheckOrigin.java @@ -62,7 +62,7 @@ public class CheckOrigin { "-XX:+UseConcMarkSweepGC", // this will cause UseParNewGC to be FLAG_SET_ERGO "-XX:+PrintGCDetails", "-XX:Flags=" + flagsFile.getAbsolutePath(), - "-cp", System.getProperty("test.class.path") + File.pathSeparator + getToolsJarPath(), + "-cp", System.getProperty("test.class.path"), "CheckOrigin", "-runtests"); @@ -137,8 +137,4 @@ public class CheckOrigin { vm.detach(); } - private static String getToolsJarPath() { - return System.getProperty("java.home") + - "/../lib/tools.jar".replace("/", File.separator); - } } diff --git a/jdk/test/com/sun/tools/attach/BasicTests.java b/jdk/test/com/sun/tools/attach/BasicTests.java index e462cf6463f..e5f4b4bd941 100644 --- a/jdk/test/com/sun/tools/attach/BasicTests.java +++ b/jdk/test/com/sun/tools/attach/BasicTests.java @@ -80,8 +80,7 @@ public class BasicTests { // Need to add jdk/lib/tools.jar to classpath. String classpath = - System.getProperty("test.class.path", "") + File.pathSeparator + - System.getProperty("test.jdk", ".") + sep + "lib" + sep + "tools.jar"; + System.getProperty("test.class.path", ""); String testClassDir = System.getProperty("test.classes", "") + sep; // Argumenta : -classpath cp BasicTests$TestMain pid agent badagent redefineagent diff --git a/jdk/test/com/sun/tools/attach/PermissionTest.java b/jdk/test/com/sun/tools/attach/PermissionTest.java index 471f6fc5953..b171fcea593 100644 --- a/jdk/test/com/sun/tools/attach/PermissionTest.java +++ b/jdk/test/com/sun/tools/attach/PermissionTest.java @@ -71,10 +71,8 @@ public class PermissionTest { private static void runTests(long pid) throws Throwable { final String sep = File.separator; - // Need to add jdk/lib/tools.jar to classpath. String classpath = - System.getProperty("test.class.path", "") + File.pathSeparator + - System.getProperty("test.jdk", ".") + sep + "lib" + sep + "tools.jar"; + System.getProperty("test.class.path", ""); String testSrc = System.getProperty("test.src", "") + sep; // Use a policy that will NOT allow attach. Test will verify exception. diff --git a/jdk/test/com/sun/tools/attach/ProviderTest.java b/jdk/test/com/sun/tools/attach/ProviderTest.java index 883951f444d..146c17f071b 100644 --- a/jdk/test/com/sun/tools/attach/ProviderTest.java +++ b/jdk/test/com/sun/tools/attach/ProviderTest.java @@ -68,11 +68,10 @@ public class ProviderTest { String testClasses = System.getProperty("test.classes", "") + sep; String jdkLib = System.getProperty("test.jdk", ".") + sep + "lib" + sep; - // Need to add SimpleProvider.jar and tools.jar to classpath. + // Need to add SimpleProvider.jar to classpath. String classpath = testClassPath + File.pathSeparator + - testClasses + "SimpleProvider.jar" + File.pathSeparator + - jdkLib + "tools.jar"; + testClasses + "SimpleProvider.jar"; String[] args = { "-classpath", diff --git a/jdk/test/com/sun/tools/attach/TempDirTest.java b/jdk/test/com/sun/tools/attach/TempDirTest.java index 86e3edda742..7aa41dc80d6 100644 --- a/jdk/test/com/sun/tools/attach/TempDirTest.java +++ b/jdk/test/com/sun/tools/attach/TempDirTest.java @@ -120,10 +120,8 @@ public class TempDirTest { private static void launchTests(long pid, Path clientTmpDir) throws Throwable { final String sep = File.separator; - // Need to add jdk/lib/tools.jar to classpath. String classpath = - System.getProperty("test.class.path", "") + File.pathSeparator + - System.getProperty("test.jdk", ".") + sep + "lib" + sep + "tools.jar"; + System.getProperty("test.class.path", ""); String[] tmpDirArg = null; if (clientTmpDir != null) { diff --git a/jdk/test/sun/management/jmxremote/bootstrap/CustomLauncherTest.java b/jdk/test/sun/management/jmxremote/bootstrap/CustomLauncherTest.java index ac0911a52ca..0f2978f9b71 100644 --- a/jdk/test/sun/management/jmxremote/bootstrap/CustomLauncherTest.java +++ b/jdk/test/sun/management/jmxremote/bootstrap/CustomLauncherTest.java @@ -145,13 +145,7 @@ public class CustomLauncherTest { ProcessBuilder client = ProcessTools.createJavaProcessBuilder( "-cp", - TEST_CLASSPATH + - File.pathSeparator + - TEST_JDK + - File.separator + - "lib" + - File.separator + - "tools.jar", + TEST_CLASSPATH, "TestManager", String.valueOf(serverPrc.getPid()), port.get(), diff --git a/jdk/test/sun/management/jmxremote/bootstrap/LocalManagementTest.java b/jdk/test/sun/management/jmxremote/bootstrap/LocalManagementTest.java index 87ab6a9245e..1306f4e31a6 100644 --- a/jdk/test/sun/management/jmxremote/bootstrap/LocalManagementTest.java +++ b/jdk/test/sun/management/jmxremote/bootstrap/LocalManagementTest.java @@ -132,13 +132,7 @@ public class LocalManagementTest { ProcessBuilder client = ProcessTools.createJavaProcessBuilder( "-cp", - TEST_CLASSPATH + - File.pathSeparator + - TEST_JDK + - File.separator + - "lib" + - File.separator + - "tools.jar", + TEST_CLASSPATH, "TestManager", String.valueOf(serverPrc.getPid()), port.get(), From 04474d85c1337e364edc909747eaa80ce6803248 Mon Sep 17 00:00:00 2001 From: Mattias Tobiasson Date: Mon, 26 Jan 2015 08:52:46 +0100 Subject: [PATCH 3/7] 8044419: TEST_BUG: com/sun/jdi/JdbReadTwiceTest.sh fails when run under root Reviewed-by: dsamersoff, sla --- jdk/test/ProblemList.txt | 3 -- jdk/test/com/sun/jdi/JdbReadTwiceTest.sh | 48 ++++++++++++++---------- 2 files changed, 29 insertions(+), 22 deletions(-) diff --git a/jdk/test/ProblemList.txt b/jdk/test/ProblemList.txt index 4954f772f8a..cbe3f74a044 100644 --- a/jdk/test/ProblemList.txt +++ b/jdk/test/ProblemList.txt @@ -292,9 +292,6 @@ com/sun/jdi/JdbMethodExitTest.sh generic-all # 8043571 com/sun/jdi/RepStep.java generic-all -# 8044419 -com/sun/jdi/JdbReadTwiceTest.sh generic-all - # 8058616 com/sun/jdi/RedefinePop.sh generic-all diff --git a/jdk/test/com/sun/jdi/JdbReadTwiceTest.sh b/jdk/test/com/sun/jdi/JdbReadTwiceTest.sh index b7ca50de096..0c78fe5ec81 100644 --- a/jdk/test/com/sun/jdi/JdbReadTwiceTest.sh +++ b/jdk/test/com/sun/jdi/JdbReadTwiceTest.sh @@ -204,27 +204,37 @@ if [ ! -r c:/ ] ; then clean fi +echo +echo "+++++++++++++++++++++++++++++++++++" +echo "Read an unreadable file - verify the read fails." -if [ ! -r c:/ ] ; then - # Can't make a file unreadable under MKS. - echo - echo "+++++++++++++++++++++++++++++++++++" - echo "Read an unreadable file - verify the read fails." - # If the file exists, we try to read it. The - # read will fail. - mkFiles $HOME/jdb.ini - id > $HOME/jdb.ini - chmod a-r $HOME/jdb.ini - if grep -q "uid=" $HOME/jdb.ini ; then - echo "Unable to make file unreadable, so test will fail. chmod: $HOME/jdb.ini" - if grep -q "uid=0" $HOME/jdb.ini ; then - echo "The test is running as root. Fix infrastructure!" - fi - fi +canMakeUnreadable=No +id > $HOME/jdb.ini +if chmod a-r $HOME/jdb.ini +then + grep -q 'uid=0(' $HOME/jdb.ini 2> /dev/null + case $? in + 0) + echo "Error! Can't make file unreadable running as root" + ;; + 1) + echo "Error! Can't make file unreadable for some other reason (windows?)" + ;; + *) + echo "OK. the file is unreadable" + canMakeUnreadable=Yes + ;; + esac +else + echo "Error! Can't create or chmod file" +fi + +if [ "$canMakeUnreadable" = "Yes" ] +then doit failIfNot 1 "open: $HOME/jdb.ini" - clean fi +clean echo @@ -246,8 +256,8 @@ echo "read $fred" > $here/jdb.ini doit failIfNot 1 "from $fred" - if [ ! -r c:/ ] ; then - # Can't make a file unreadable under MKS + if [ "$canMakeUnreadable" = "Yes" ] + then chmod a-r $fred doit failIfNot 1 "open: $fred" From 1d41a44b78a75f60d778070da669fdca59f30319 Mon Sep 17 00:00:00 2001 From: Katja Kantserova Date: Mon, 26 Jan 2015 13:50:53 +0100 Subject: [PATCH 4/7] 8071324: com/sun/jdi/ConnectedVMs.java should be quarantined Reviewed-by: sla --- jdk/test/ProblemList.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/jdk/test/ProblemList.txt b/jdk/test/ProblemList.txt index cbe3f74a044..c83e162b72a 100644 --- a/jdk/test/ProblemList.txt +++ b/jdk/test/ProblemList.txt @@ -298,6 +298,9 @@ com/sun/jdi/RedefinePop.sh generic-all # 8068645 com/sun/jdi/CatchPatternTest.sh generic-all +# 8069402 +com/sun/jdi/ConnectedVMs.java generic-all + ############################################################################ # jdk_util From 3de0140a8895369bbb22e36254071f0408840005 Mon Sep 17 00:00:00 2001 From: Serguei Spitsyn Date: Mon, 26 Jan 2015 15:46:47 -0800 Subject: [PATCH 5/7] 8068162: jvmtiRedefineClasses.cpp: guarantee(false) failed: OLD and/or OBSOLETE method(s) found Enable the test: java/lang/instrument/IsModifiableClassAgent.java Reviewed-by: coleenp, dcubed --- jdk/test/java/lang/instrument/IsModifiableClassAgent.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/jdk/test/java/lang/instrument/IsModifiableClassAgent.java b/jdk/test/java/lang/instrument/IsModifiableClassAgent.java index 31329dfd3a1..bb2130924ba 100644 --- a/jdk/test/java/lang/instrument/IsModifiableClassAgent.java +++ b/jdk/test/java/lang/instrument/IsModifiableClassAgent.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2007, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 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 @@ -23,7 +23,6 @@ /** * @test - * @ignore JDK-8068162 * @bug 6331574 * @summary test isModifiableClass * @author Robert Field, Sun Microsystems From 04d8bf6c334f8ec38d2c93e227af0673a3e78386 Mon Sep 17 00:00:00 2001 From: Katja Kantserova Date: Wed, 28 Jan 2015 09:23:41 +0100 Subject: [PATCH 6/7] 8071582: com/sun/jdi/GetLocalVariables4Test.sh should be quarantined Reviewed-by: sspitsyn, sla --- jdk/test/ProblemList.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/jdk/test/ProblemList.txt b/jdk/test/ProblemList.txt index c83e162b72a..7b0a4f7a035 100644 --- a/jdk/test/ProblemList.txt +++ b/jdk/test/ProblemList.txt @@ -301,6 +301,9 @@ com/sun/jdi/CatchPatternTest.sh generic-all # 8069402 com/sun/jdi/ConnectedVMs.java generic-all +# 8067354 +com/sun/jdi/GetLocalVariables4Test.sh windows-all + ############################################################################ # jdk_util From b5f5424c83e28240a3eb280251ffe604a294ac20 Mon Sep 17 00:00:00 2001 From: Daniel Fuchs Date: Wed, 28 Jan 2015 17:48:59 +0100 Subject: [PATCH 7/7] 8068730: Increase the precision of the implementation of java.time.Clock.systemUTC() Changes the implementation of java.time.Clock.systemUTC() to take advantage of the maximum resolution of the underlying native clock on which System.currentTimeMillis() is based. Reviewed-by: dholmes, rriggs, scolebourne, sla --- .../share/classes/java/time/Clock.java | 64 +++- .../share/classes/java/util/Formatter.java | 10 +- .../java.base/share/classes/sun/misc/VM.java | 32 +- jdk/src/java.base/share/native/include/jvm.h | 5 +- jdk/src/java.base/share/native/libjava/VM.c | 15 +- .../time/tck/java/time/TCKLocalDateTime.java | 5 +- .../java/time/tck/java/time/TCKLocalTime.java | 5 +- .../time/tck/java/time/TCKZonedDateTime.java | 5 +- .../time/test/java/time/TestClock_System.java | 298 +++++++++++++++++- .../time/test/java/util/TestFormatter.java | 41 ++- .../sun/misc/VM/GetNanoTimeAdjustment.java | 249 +++++++++++++++ 11 files changed, 714 insertions(+), 15 deletions(-) create mode 100644 jdk/test/sun/misc/VM/GetNanoTimeAdjustment.java diff --git a/jdk/src/java.base/share/classes/java/time/Clock.java b/jdk/src/java.base/share/classes/java/time/Clock.java index b1127848bed..28dd55f9b8d 100644 --- a/jdk/src/java.base/share/classes/java/time/Clock.java +++ b/jdk/src/java.base/share/classes/java/time/Clock.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 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 @@ -61,12 +61,15 @@ */ package java.time; +import java.io.IOException; +import java.io.ObjectInputStream; import static java.time.LocalTime.NANOS_PER_MINUTE; import static java.time.LocalTime.NANOS_PER_SECOND; import java.io.Serializable; import java.util.Objects; import java.util.TimeZone; +import sun.misc.VM; /** * A clock providing access to the current instant, date and time using a time-zone. @@ -446,10 +449,22 @@ public abstract class Clock { */ static final class SystemClock extends Clock implements Serializable { private static final long serialVersionUID = 6740630888130243051L; + private static final long OFFSET_SEED = + System.currentTimeMillis()/1000 - 1024; // initial offest private final ZoneId zone; + // We don't actually need a volatile here. + // We don't care if offset is set or read concurrently by multiple + // threads - we just need a value which is 'recent enough' - in other + // words something that has been updated at least once in the last + // 2^32 secs (~136 years). And even if we by chance see an invalid + // offset, the worst that can happen is that we will get a -1 value + // from getNanoTimeAdjustment, forcing us to update the offset + // once again. + private transient long offset; SystemClock(ZoneId zone) { this.zone = zone; + this.offset = OFFSET_SEED; } @Override public ZoneId getZone() { @@ -464,11 +479,50 @@ public abstract class Clock { } @Override public long millis() { + // System.currentTimeMillis() and VM.getNanoTimeAdjustment(offset) + // use the same time source - System.currentTimeMillis() simply + // limits the resolution to milliseconds. + // So we take the faster path and call System.currentTimeMillis() + // directly - in order to avoid the performance penalty of + // VM.getNanoTimeAdjustment(offset) which is less efficient. return System.currentTimeMillis(); } @Override public Instant instant() { - return Instant.ofEpochMilli(millis()); + // Take a local copy of offset. offset can be updated concurrently + // by other threads (even if we haven't made it volatile) so we will + // work with a local copy. + long localOffset = offset; + long adjustment = VM.getNanoTimeAdjustment(localOffset); + + if (adjustment == -1) { + // -1 is a sentinel value returned by VM.getNanoTimeAdjustment + // when the offset it is given is too far off the current UTC + // time. In principle, this should not happen unless the + // JVM has run for more than ~136 years (not likely) or + // someone is fiddling with the system time, or the offset is + // by chance at 1ns in the future (very unlikely). + // We can easily recover from all these conditions by bringing + // back the offset in range and retry. + + // bring back the offset in range. We use -1024 to make + // it more unlikely to hit the 1ns in the future condition. + localOffset = System.currentTimeMillis()/1000 - 1024; + + // retry + adjustment = VM.getNanoTimeAdjustment(localOffset); + + if (adjustment == -1) { + // Should not happen: we just recomputed a new offset. + // It should have fixed the issue. + throw new InternalError("Offset " + localOffset + " is not in range"); + } else { + // OK - recovery succeeded. Update the offset for the + // next call... + offset = localOffset; + } + } + return Instant.ofEpochSecond(localOffset, adjustment); } @Override public boolean equals(Object obj) { @@ -485,6 +539,12 @@ public abstract class Clock { public String toString() { return "SystemClock[" + zone + "]"; } + private void readObject(ObjectInputStream is) + throws IOException, ClassNotFoundException { + // ensure that offset is initialized + is.defaultReadObject(); + offset = OFFSET_SEED; + } } //----------------------------------------------------------------------- diff --git a/jdk/src/java.base/share/classes/java/util/Formatter.java b/jdk/src/java.base/share/classes/java/util/Formatter.java index a973ee6889e..3b751b6e86a 100644 --- a/jdk/src/java.base/share/classes/java/util/Formatter.java +++ b/jdk/src/java.base/share/classes/java/util/Formatter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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 @@ -57,6 +57,7 @@ import java.time.ZoneOffset; import java.time.temporal.ChronoField; import java.time.temporal.TemporalAccessor; import java.time.temporal.TemporalQueries; +import java.time.temporal.UnsupportedTemporalTypeException; import sun.misc.DoubleConsts; import sun.misc.FormattedFloatingDecimal; @@ -4056,7 +4057,12 @@ public final class Formatter implements Closeable, Flushable { break; } case DateTime.NANOSECOND: { // 'N' (000000000 - 999999999) - int i = t.get(ChronoField.MILLI_OF_SECOND) * 1000000; + int i; + try { + i = t.get(ChronoField.NANO_OF_SECOND); + } catch (UnsupportedTemporalTypeException u) { + i = t.get(ChronoField.MILLI_OF_SECOND) * 1000000; + } Flags flags = Flags.ZERO_PAD; sb.append(localizedMagnitude(null, i, flags, 9, l)); break; diff --git a/jdk/src/java.base/share/classes/sun/misc/VM.java b/jdk/src/java.base/share/classes/sun/misc/VM.java index c877e40446f..36bfb606495 100644 --- a/jdk/src/java.base/share/classes/sun/misc/VM.java +++ b/jdk/src/java.base/share/classes/sun/misc/VM.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 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 @@ -402,6 +402,36 @@ public class VM { */ public static native long getegid(); + /** + * Get a nanosecond time stamp adjustment in the form of a single long. + * + * This value can be used to create an instant using + * {@link java.time.Instant#ofEpochSecond(long, long) + * java.time.Instant.ofEpochSecond(offsetInSeconds, + * getNanoTimeAdjustment(offsetInSeconds))}. + *

+ * The value returned has the best resolution available to the JVM on + * the current system. + * This is usually down to microseconds - or tenth of microseconds - + * depending on the OS/Hardware and the JVM implementation. + * + * @param offsetInSeconds The offset in seconds from which the nanosecond + * time stamp should be computed. + * + * @apiNote The offset should be recent enough - so that + * {@code offsetInSeconds} is within {@code +/- 2^32} seconds of the + * current UTC time. If the offset is too far off, {@code -1} will be + * returned. As such, {@code -1} must not be considered as a valid + * nano time adjustment, but as an exception value indicating + * that an offset closer to the current time should be used. + * + * @return A nanosecond time stamp adjustment in the form of a single long. + * If the offset is too far off the current time, this method returns -1. + * In that case, the caller should call this method again, passing a + * more accurate offset. + */ + public static native long getNanoTimeAdjustment(long offsetInSeconds); + static { initialize(); } diff --git a/jdk/src/java.base/share/native/include/jvm.h b/jdk/src/java.base/share/native/include/jvm.h index 514378df9b2..7177f2e1d1e 100644 --- a/jdk/src/java.base/share/native/include/jvm.h +++ b/jdk/src/java.base/share/native/include/jvm.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -104,6 +104,9 @@ JVM_CurrentTimeMillis(JNIEnv *env, jclass ignored); JNIEXPORT jlong JNICALL JVM_NanoTime(JNIEnv *env, jclass ignored); +JNIEXPORT jlong JNICALL +JVM_GetNanoTimeAdjustment(JNIEnv *env, jclass ignored, jlong offset_secs); + JNIEXPORT void JNICALL JVM_ArrayCopy(JNIEnv *env, jclass ignored, jobject src, jint src_pos, jobject dst, jint dst_pos, jint length); diff --git a/jdk/src/java.base/share/native/libjava/VM.c b/jdk/src/java.base/share/native/libjava/VM.c index 1968a06b01b..14fd1a63487 100644 --- a/jdk/src/java.base/share/native/libjava/VM.c +++ b/jdk/src/java.base/share/native/libjava/VM.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2004, 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 @@ -33,6 +33,11 @@ #include "sun_misc_VM.h" +/* Only register the performance-critical methods */ +static JNINativeMethod methods[] = { + {"getNanoTimeAdjustment", "(J)J", (void *)&JVM_GetNanoTimeAdjustment} +}; + JNIEXPORT jobject JNICALL Java_sun_misc_VM_latestUserDefinedLoader(JNIEnv *env, jclass cls) { return JVM_LatestUserDefinedLoader(env); @@ -49,6 +54,14 @@ Java_sun_misc_VM_initialize(JNIEnv *env, jclass cls) { return; } + // Registers implementations of native methods described in methods[] + // above. + // In particular, registers JVM_GetNanoTimeAdjustment as the implementation + // of the native sun.misc.VM.getNanoTimeAdjustment - avoiding the cost of + // introducing a Java_sun_misc_VM_getNanoTimeAdjustment wrapper + (*env)->RegisterNatives(env, cls, + methods, sizeof(methods)/sizeof(methods[0])); + func_p = (GetJvmVersionInfo_fp) JDK_FindJvmEntry("JVM_GetVersionInfo"); if (func_p != NULL) { jvm_version_info info; diff --git a/jdk/test/java/time/tck/java/time/TCKLocalDateTime.java b/jdk/test/java/time/tck/java/time/TCKLocalDateTime.java index 40ed5347589..51b1163dbca 100644 --- a/jdk/test/java/time/tck/java/time/TCKLocalDateTime.java +++ b/jdk/test/java/time/tck/java/time/TCKLocalDateTime.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 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 @@ -292,7 +292,8 @@ public class TCKLocalDateTime extends AbstractDateTimeTest { expected = LocalDateTime.now(Clock.system(zone)); test = LocalDateTime.now(zone); } - assertEquals(test, expected); + assertEquals(test.truncatedTo(ChronoUnit.SECONDS), + expected.truncatedTo(ChronoUnit.SECONDS)); } //----------------------------------------------------------------------- diff --git a/jdk/test/java/time/tck/java/time/TCKLocalTime.java b/jdk/test/java/time/tck/java/time/TCKLocalTime.java index f79e8fc41f6..88f99ca8c65 100644 --- a/jdk/test/java/time/tck/java/time/TCKLocalTime.java +++ b/jdk/test/java/time/tck/java/time/TCKLocalTime.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 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 @@ -246,7 +246,8 @@ public class TCKLocalTime extends AbstractDateTimeTest { expected = LocalTime.now(Clock.system(zone)); test = LocalTime.now(zone); } - assertEquals(test, expected); + assertEquals(test.truncatedTo(ChronoUnit.SECONDS), + expected.truncatedTo(ChronoUnit.SECONDS)); } //----------------------------------------------------------------------- diff --git a/jdk/test/java/time/tck/java/time/TCKZonedDateTime.java b/jdk/test/java/time/tck/java/time/TCKZonedDateTime.java index fb743e87b14..11ace2decf4 100644 --- a/jdk/test/java/time/tck/java/time/TCKZonedDateTime.java +++ b/jdk/test/java/time/tck/java/time/TCKZonedDateTime.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 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 @@ -260,7 +260,8 @@ public class TCKZonedDateTime extends AbstractDateTimeTest { expected = ZonedDateTime.now(Clock.system(zone)); test = ZonedDateTime.now(zone); } - assertEquals(test, expected); + assertEquals(test.truncatedTo(ChronoUnit.SECONDS), + expected.truncatedTo(ChronoUnit.SECONDS)); } //----------------------------------------------------------------------- diff --git a/jdk/test/java/time/test/java/time/TestClock_System.java b/jdk/test/java/time/test/java/time/TestClock_System.java index 881a76060a1..527008920cc 100644 --- a/jdk/test/java/time/test/java/time/TestClock_System.java +++ b/jdk/test/java/time/test/java/time/TestClock_System.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 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 @@ -62,7 +62,9 @@ package test.java.time; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertSame; +import java.lang.reflect.Field; import java.time.Clock; +import java.time.Instant; import java.time.ZoneId; import org.testng.annotations.Test; @@ -87,4 +89,298 @@ public class TestClock_System { assertEquals(test.toString(), "SystemClock[Europe/Paris]"); } + //----------------------------------------------------------------------- + + private static String formatTime(String prefix, Instant time) { + return prefix + ": " + time + " - seconds: " + + time.getEpochSecond() + ", nanos: " + + time.getNano(); + } + + public void test_ClockResolution() { + Clock highestUTC = Clock.systemUTC(); + + Instant start = Instant.ofEpochMilli(System.currentTimeMillis()); + + try { + // smoke test + Instant system1 = Instant.ofEpochMilli(System.currentTimeMillis()); + Instant system2 = Instant.ofEpochMilli(System.currentTimeMillis()); + Instant highest1 = highestUTC.instant(); + Instant highest2 = highestUTC.instant(); + System.out.println(formatTime("\nsystemUTC #1 ", system1)); + System.out.println(formatTime("systemUTC #2 ", system2)); + System.out.println(formatTime("highestResolutionUTC #1 ", highest1)); + System.out.println(formatTime("highestResolutionUTC #2 ", highest2)); + + if (system2.isBefore(system1)) { + System.err.println("system2 is before system1!"); + System.err.println(formatTime("\n\tsystem1", system1)); + System.err.println(formatTime("\n\tsystem2", system2)); + throw new RuntimeException("system2 is before system1!" + + formatTime("\n\tsystem1", system1) + + formatTime("\n\tsystem2", system2)); + } + if (highest2.isBefore(highest1)) { + System.err.println("highest2 is before highest1!"); + System.err.println(formatTime("\n\thighest1", system1)); + System.err.println(formatTime("\n\tsystem2", highest2)); + throw new RuntimeException("highest2 is before system1!" + + formatTime("\n\thighest1", system1) + + formatTime("\n\tsystem2", highest2)); + } + + // better test - but depends on implementation details. + // we're not rounding - so highest1 should be greater or equal to + // system1 + system1 = Instant.ofEpochMilli(System.currentTimeMillis()); + highest1 = highestUTC.instant(); + + System.out.println(formatTime("\nsystemUTC ", system1)); + System.out.println(formatTime("highestResolutionUTC ", highest1)); + + if (highest1.isBefore(system1)) { + System.err.println("highest1 is before system1!"); + System.err.println(formatTime("\n\tsystem1", system1)); + System.err.println(formatTime("\n\thighest1", highest1)); + throw new RuntimeException("highest1 is before system1!" + + formatTime("\n\tsystem1", system1) + + formatTime("\n\thighest1", highest1)); + } + + int count=0; + // let's preheat the system a bit: + for (int i = 0; i < 1000 ; i++) { + system1 = Instant.ofEpochMilli(System.currentTimeMillis()); + highest1 = highestUTC.instant(); + final int sysnan = system1.getNano(); + final int nanos = highest1.getNano(); + if ((nanos % 1000000) > 0) { + count++; // we have micro seconds + } + if ((sysnan % 1000000) > 0) { + throw new RuntimeException("Expected only millisecconds " + + "precision for systemUTC, found " + + (sysnan % 1000000) + " remainder."); + } + } + System.out.println("\nNumber of time stamps which had better than" + + " millisecond precision: "+count+"/"+1000); + System.out.println(formatTime("\nsystemUTC ", system1)); + System.out.println(formatTime("highestResolutionUTC ", highest1)); + if (count == 0) { + System.err.println("Something is strange: no microsecond " + + "precision with highestResolutionUTC?"); + throw new RuntimeException("Micro second preccision not reached"); + } + + // check again + if (highest1.isBefore(system1)) { + System.err.println("highest1 is before system1!"); + System.err.println(formatTime("\n\tsystem1", system1)); + System.err.println(formatTime("\n\thighest1", highest1)); + throw new RuntimeException("highest1 is before system1!" + + formatTime("\n\tsystem1", system1) + + formatTime("\n\thighest1", highest1)); + } + + // leap of faith: ensure that highest1 is from within 10 secs of + // system1 + if (highest1.toEpochMilli() != system1.toEpochMilli()) { + long delta = highest1.getEpochSecond() - system1.getEpochSecond(); + if (delta > 10) { + throw new RuntimeException("Unexpected long delay between two clocks (" + + delta + " seconds)" + + formatTime("\n\t system1", system1) + + formatTime("\n\t highest1", highest1)); + + } + } else { + System.out.println("You won the lottery: the two dates are within 1 millisecond!\n"); + } + + } finally { + Instant stop = Instant.ofEpochMilli(System.currentTimeMillis()); + if (start.isAfter(stop)) { + // This should not happen - but can (un)probably be observed + // when switching to summer time, or if another application + // is switching the system date... + System.err.println("Cannot test - date was setback: " + + formatTime("\n\tstarted at", start) + + formatTime("\n\tstopped at", stop) + "\n"); + return; // will prevent exceptions from being propagated. + } + } + } + + static final long MAX_OFFSET = 0x0100000000L; + static final long MIN_OFFSET = -MAX_OFFSET; + + // A helper class to test that SystemClock correctly recomputes + // its offset. + static class SystemClockOffset { + + 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 Clock systemUTC = Clock.systemUTC(); + static final Field offsetField; + + static { + try { + offsetField = Class.forName("java.time.Clock$SystemClock").getDeclaredField("offset"); + offsetField.setAccessible(true); + } catch (ClassNotFoundException | NoSuchFieldException ex) { + throw new ExceptionInInitializerError(ex); + } + } + + 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) + throws IllegalAccessException { + testWithOffset(name, offset, systemUTC); + } + + static void testWithOffset(String name, long offset, Clock clock) + throws IllegalAccessException { + offsetField.set(clock, offset); + long beforeMillis = System.currentTimeMillis(); + final Instant instant = clock.instant(); + long afterMillis = System.currentTimeMillis(); + long actualOffset = offsetField.getLong(clock); + long instantMillis = instant.getEpochSecond() * MILLIS_IN_SECOND + + instant.getNano() / NANOS_IN_MILLI; + if (instantMillis < beforeMillis || instantMillis > afterMillis) { + throw new RuntimeException(name + + ": Invalid instant: " + instant + + " (~" + instantMillis + "ms)" + + " when time in millis is in [" + + beforeMillis + ", " + afterMillis + + "] and offset in seconds is " + offset); + } + Answer isOffLimits = isOffLimits(beforeMillis / MILLIS_IN_SECOND, + afterMillis / MILLIS_IN_SECOND, offset); + switch (isOffLimits) { + case YES: + if (actualOffset == offset) { + throw new RuntimeException(name + + ": offset was offlimit but was not recomputed " + + " when time in millis is in [" + + beforeMillis + ", " + afterMillis + + "] and offset in seconds was " + offset); + } + break; + case NO: + if (actualOffset != offset) { + throw new RuntimeException(name + + ": offset was not offlimit but was recomputed."); + } + break; + default: + break; + } + if (distance(actualOffset, instant.getEpochSecond()) >= MAX_OFFSET) { + throw new RuntimeException(name + ": Actual offset is too far off:" + + " offset=" + actualOffset + + "instant.seconds=" + instant.getEpochSecond()); + } + long adjustment = (instant.getEpochSecond() - actualOffset) * NANOS_IN_SECOND + + instant.getNano(); + validateAdjustment(name, actualOffset, beforeMillis, afterMillis, adjustment); + } + + static void validateAdjustment(String name, long offset, long beforeMillis, + long afterMillis, long adjustment) { + 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); + } + } + } + + public void test_OffsetRegular() throws IllegalAccessException { + System.out.println("*** Testing regular cases ***"); + SystemClockOffset.testWithOffset("System.currentTimeMillis()/1000", + System.currentTimeMillis()/1000); + SystemClockOffset.testWithOffset("System.currentTimeMillis()/1000 - 1024", + System.currentTimeMillis()/1000 - 1024); + SystemClockOffset.testWithOffset("System.currentTimeMillis()/1000 + 1024", + System.currentTimeMillis()/1000 + 1024); + } + + public void test_OffsetLimits() throws IllegalAccessException { + System.out.println("*** Testing limits ***"); + SystemClockOffset.testWithOffset("System.currentTimeMillis()/1000 - MAX_OFFSET + 1", + System.currentTimeMillis()/1000 - MAX_OFFSET + 1); + SystemClockOffset.testWithOffset("System.currentTimeMillis()/1000 + MAX_OFFSET - 1", + System.currentTimeMillis()/1000 + MAX_OFFSET - 1); + SystemClockOffset.testWithOffset("System.currentTimeMillis()/1000 - MAX_OFFSET", + System.currentTimeMillis()/1000 - MAX_OFFSET); + SystemClockOffset.testWithOffset("System.currentTimeMillis()/1000 + MAX_OFFSET", + System.currentTimeMillis()/1000 + MAX_OFFSET); + SystemClockOffset.testWithOffset("System.currentTimeMillis()/1000 - MAX_OFFSET - 1024", + System.currentTimeMillis()/1000 - MAX_OFFSET - 1024); + SystemClockOffset.testWithOffset("System.currentTimeMillis()/1000 + MAX_OFFSET + 1024", + System.currentTimeMillis()/1000 + MAX_OFFSET + 1024); + SystemClockOffset.testWithOffset("0", 0); + SystemClockOffset.testWithOffset("-1", -1); + SystemClockOffset.testWithOffset("Integer.MAX_VALUE + System.currentTimeMillis()/1000", + ((long)Integer.MAX_VALUE) + System.currentTimeMillis()/1000); + SystemClockOffset.testWithOffset("System.currentTimeMillis()/1000 - Integer.MIN_VALUE", + System.currentTimeMillis()/1000 - Integer.MIN_VALUE); + SystemClockOffset.testWithOffset("Long.MAX_VALUE", Long.MAX_VALUE); + SystemClockOffset.testWithOffset("System.currentTimeMillis()/1000 - Long.MIN_VALUE", + (Long.MIN_VALUE + System.currentTimeMillis()/1000)*-1); + } } diff --git a/jdk/test/java/time/test/java/util/TestFormatter.java b/jdk/test/java/time/test/java/util/TestFormatter.java index 67a73cee697..5cf2163412c 100644 --- a/jdk/test/java/time/test/java/util/TestFormatter.java +++ b/jdk/test/java/time/test/java/util/TestFormatter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 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 @@ -38,6 +38,7 @@ import java.time.chrono.Chronology; import java.time.temporal.ChronoField; import java.time.temporal.TemporalQueries; import java.time.temporal.TemporalAccessor; +import java.time.temporal.UnsupportedTemporalTypeException; import java.util.*; @@ -153,6 +154,44 @@ public class TestFormatter { if (verbose) { System.out.printf("%-24s : %s%n", getClassName(dt), out); } + + // expected usually comes from Calendar which only has milliseconds + // precision. So we're going to replace it's N:[nanos] stamp with + // the correct value for nanos. + if ((dt instanceof TemporalAccessor) && expected != null) { + try { + // Get millis & nanos from the dt + final TemporalAccessor ta = (TemporalAccessor) dt; + final int nanos = ta.get(ChronoField.NANO_OF_SECOND); + final int millis = ta.get(ChronoField.MILLI_OF_SECOND); + final String nanstr = String.valueOf(nanos); + final String mistr = String.valueOf(millis); + + // Compute the value of the N:[nanos] field that we expect + // to find in 'out' + final StringBuilder sb = new StringBuilder(); + sb.append("N:["); + for (int i=nanstr.length(); i<9; i++) { + sb.append('0'); + } + sb.append(nanos).append("]"); + + // Compute the truncated value of N:[nanos] field that might + // be in 'expected' when expected was built from Calendar. + final StringBuilder sbm = new StringBuilder(); + sbm.append("N:["); + for (int i=mistr.length(); i<3; i++) { + sbm.append('0'); + } + sbm.append(mistr).append("000000]"); + + // if expected contains the truncated value, replace it with + // the complete value. + expected = expected.replace(sbm.toString(), sb.toString()); + } catch (UnsupportedTemporalTypeException e) { + // nano seconds unsupported - nothing to do... + } + } if (expected != null && !out.equals(expected)) { System.out.printf("%-24s actual: %s%n FAILED; expected: %s%n", getClassName(dt), out, expected); diff --git a/jdk/test/sun/misc/VM/GetNanoTimeAdjustment.java b/jdk/test/sun/misc/VM/GetNanoTimeAdjustment.java new file mode 100644 index 00000000000..bfeb8c0c7f3 --- /dev/null +++ b/jdk/test/sun/misc/VM/GetNanoTimeAdjustment.java @@ -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(); + } + +}