8196565: AIX: Clean up os::javaTimeNanos according to AIX/PASE specification
authorclanger
Wed, 07 Feb 2018 16:03:12 +0100
changeset 48876 45b6aae769cc
parent 48875 c1e7612f6b11
child 48877 e4d80042ff19
8196565: AIX: Clean up os::javaTimeNanos according to AIX/PASE specification Reviewed-by: stuefe, dholmes
src/hotspot/os/aix/os_aix.cpp
--- a/src/hotspot/os/aix/os_aix.cpp	Tue Feb 06 15:48:50 2018 +0100
+++ b/src/hotspot/os/aix/os_aix.cpp	Wed Feb 07 16:03:12 2018 +0100
@@ -188,6 +188,7 @@
 ////////////////////////////////////////////////////////////////////////////////
 // local variables
 
+static volatile jlong max_real_time = 0;
 static jlong    initial_time_count = 0;
 static int      clock_tics_per_sec = 100;
 static sigset_t check_signal_done;         // For diagnostics to print a message once (see run_periodic_checks)
@@ -1076,32 +1077,50 @@
   nanos = jlong(time.tv_usec) * 1000;
 }
 
+// We use mread_real_time here.
+// On AIX: If the CPU has a time register, the result will be RTC_POWER and
+// it has to be converted to real time. AIX documentations suggests to do
+// this unconditionally, so we do it.
+//
+// See: https://www.ibm.com/support/knowledgecenter/ssw_aix_61/com.ibm.aix.basetrf2/read_real_time.htm
+//
+// On PASE: mread_real_time will always return RTC_POWER_PC data, so no
+// conversion is necessary. However, mread_real_time will not return
+// monotonic results but merely matches read_real_time. So we need a tweak
+// to ensure monotonic results.
+//
+// For PASE no public documentation exists, just word by IBM
 jlong os::javaTimeNanos() {
+  timebasestruct_t time;
+  int rc = mread_real_time(&time, TIMEBASE_SZ);
   if (os::Aix::on_pase()) {
-
-    timeval time;
-    int status = gettimeofday(&time, NULL);
-    assert(status != -1, "PASE error at gettimeofday()");
-    jlong usecs = jlong((unsigned long long) time.tv_sec * (1000 * 1000) + time.tv_usec);
-    return 1000 * usecs;
-
+    assert(rc == RTC_POWER, "expected time format RTC_POWER from mread_real_time in PASE");
+    jlong now = jlong(time.tb_high) * NANOSECS_PER_SEC + jlong(time.tb_low);
+    jlong prev = max_real_time;
+    if (now <= prev) {
+      return prev;   // same or retrograde time;
+    }
+    jlong obsv = Atomic::cmpxchg(now, &max_real_time, prev);
+    assert(obsv >= prev, "invariant");   // Monotonicity
+    // If the CAS succeeded then we're done and return "now".
+    // If the CAS failed and the observed value "obsv" is >= now then
+    // we should return "obsv".  If the CAS failed and now > obsv > prv then
+    // some other thread raced this thread and installed a new value, in which case
+    // we could either (a) retry the entire operation, (b) retry trying to install now
+    // or (c) just return obsv.  We use (c).   No loop is required although in some cases
+    // we might discard a higher "now" value in deference to a slightly lower but freshly
+    // installed obsv value.   That's entirely benign -- it admits no new orderings compared
+    // to (a) or (b) -- and greatly reduces coherence traffic.
+    // We might also condition (c) on the magnitude of the delta between obsv and now.
+    // Avoiding excessive CAS operations to hot RW locations is critical.
+    // See https://blogs.oracle.com/dave/entry/cas_and_cache_trivia_invalidate
+    return (prev == obsv) ? now : obsv;
   } else {
-    // On AIX use the precision of processors real time clock
-    // or time base registers.
-    timebasestruct_t time;
-    int rc;
-
-    // If the CPU has a time register, it will be used and
-    // we have to convert to real time first. After convertion we have following data:
-    // time.tb_high [seconds since 00:00:00 UTC on 1.1.1970]
-    // time.tb_low  [nanoseconds after the last full second above]
-    // We better use mread_real_time here instead of read_real_time
-    // to ensure that we will get a monotonic increasing time.
-    if (mread_real_time(&time, TIMEBASE_SZ) != RTC_POWER) {
+    if (rc != RTC_POWER) {
       rc = time_base_to_time(&time, TIMEBASE_SZ);
-      assert(rc != -1, "aix error at time_base_to_time()");
+      assert(rc != -1, "error calling time_base_to_time()");
     }
-    return jlong(time.tb_high) * (1000 * 1000 * 1000) + jlong(time.tb_low);
+    return jlong(time.tb_high) * NANOSECS_PER_SEC + jlong(time.tb_low);
   }
 }