RFR (M): 8235741: Inappropriate uses of os::javaTimeMillis()

David Holmes david.holmes at oracle.com
Thu Jan 16 04:26:02 UTC 2020


While awaiting further comments/feedback I've made some updates:

- added comments to existing use of javaTimeMillis() where it wasn't 
(fairly) obvious why it was being used
- fixed now incorrect comment pointed out by Kim
- copied os_per_linux.cpp changes to os_perf_aix.cpp (optimistically 
hoping for a thumbs up from Erik G. on this part :) ).

Full and incr webrevs at:

http://cr.openjdk.java.net/~dholmes/8235741/webrev.v2/
http://cr.openjdk.java.net/~dholmes/8235741/webrev.v2-incr/

Thanks,
David

On 15/01/2020 5:12 pm, David Holmes wrote:
> Hi Kim,
> 
> Thanks for taking a look at this.
> 
> On 15/01/2020 4:24 pm, Kim Barrett wrote:
>>> On Jan 13, 2020, at 2:13 AM, David Holmes <david.holmes at oracle.com> 
>>> wrote:
>>>
>>> webrev: http://cr.openjdk.java.net/~dholmes/8235741/webrev/
>>> bug: https://bugs.openjdk.java.net/browse/JDK-8235741
>>>
>>> Full details in the bug report about the existing uses of 
>>> javaTimeMillis(), many of which just want an elapsed time in ms and 
>>> so should be using javaTimeNanos() and convert to ms. This covers 
>>> areas all across the VM.
>>>
>>> Only non-simple change is in os_perf_linux.cpp (and the same code 
>>> will be in os_perf_aix.cpp once it has been validated). There we are 
>>> tracking an elapsed time in ms but relative to the boot time, which 
>>> is seconds since the epoch. Consequently the first interval has to be 
>>> calculated using javaTimeMillis, but after that we can use 
>>> javaTimeNanos (using a new 'first time' captured at the same time we 
>>> used javaTimeMillis). I think I have the logic right but other than 
>>> through JFR this code seems unused and I have limited means of 
>>> testing it. The JFR test 
>>> jdk/jfr/event/os/TestThreadContextSwitches.java exercises the code 
>>> but the results of running that test seems to exhibit arbitrary 
>>> randomness in the rates reported - e.g. 0 to 16000Hz - both with and 
>>> without my change, so not really that useful. Stefan K. suggested a 
>>> gtest which I may look into - though it is frustrating to have to 
>>> expend such effort to validate this.
>>>
>>> Other testing tiers 1-3.
>>>
>>> Thanks,
>>> David
>>
>> Thanks for the audit of uses of os::javaTimeMillis() in the bug report.
>> I wonder if some of that ought to be captured as comments in the
>> relevant code.  It's not always obvious to me that an external time
>> base is involved and thus making javaTimeMillis not a mistake.
> 
> Okay, I will add comments to the other uses of currentTimeMillis().
> 
>> There are a lot of places where conversions from nanoseconds to
>> milliseconds are being done to maintain existing units.  Some of those
>> places look like they could just as well be in nanoseconds.  But I can
>> see how changing the units for some of those could lead to a lot of
>> fannout, so okay.
> 
> Yes I tried to minimise the changes. In many cases a granularity of ms 
> seems somewhat arbitrary.
> 
>> ------------------------------------------------------------------------------ 
>>
>> src/hotspot/os/windows/os_perf_windows.cpp
>>   100   s8     lastUpdate; // Last time query was updated (current 
>> millis).
>> ...
>>   290   const s8 now = os::javaTimeNanos();
>>   291   if (NANOS_TO_MILLIS(now - update_query->lastUpdate) > 
>> min_update_interval_millis) {
>> ...
>>   295     update_query->lastUpdate = now;
>>
>> now and update_query->lastUpdate are now in nanos, but comment for
>> lastUpdate still says it's in millis.  Looks like the comment needs
>> updating.
> 
> Yes - good catch.
> 
>> ------------------------------------------------------------------------------ 
>>
>> src/hotspot/share/utilities/globalDefinitions.hpp
>>   262 // time unit conversion macros
>>   263
>>   264 #define NANOS_TO_MILLIS(ns) ((ns) / NANOSECS_PER_MILLISEC)
>>   265 #define MILLIS_TO_NANOS(ms) ((ms) * NANOSECS_PER_MILLISEC)
>>
>> Why are these macros, rather than (template) functions?
> 
> Just because I just wanted a simple textual replacement to make it 
> clearer that I'm converting from millis to nanos or vice versa. I reach 
> for macros for such simple cases.
> 
>> Also, depending on the type and value of ms, MILLIS_TO_NANOS could
>> easily overflow, e.g. if ms type is a 32 bit type with a value of more
>> than ~4 seconds.  (I checked the two uses, and they happen to be okay.)
> 
> These are not trying to be mathematically sound. The conversion from 
> millis to nanos is used in two cases:
> 
> 1. Converting a current timestamp in ms to ns. Unless the current time 
> is set far in the future I don't think we have any issue with overflow 
> of  such a value.
> 
> 2. converting an elapsed time in ms to ns. These will be small values so 
> no overflow is possible.
> 
>> inline int64_t nanos_to_millis(int64_t ns) {
>>    return ns / NANOSECS_PER_MILLISECOND;
>> }
>>
>> inline int64_t millis_to_nanos(int64_t ms) {
>>    return ms * NANOSECONDS_PER_MILLISEC;
>> }
>>
>> Also, the names don't suggest time conversions, but potentially
>> arbitrary unit conversions, e.g. between something in NANOUNITS and
>> something in MILLIUNITS.
> 
> They don't have to be time conversions - the calculation is unit-less in 
> practice. The fact we have NANOSEC_PER_MILLISECOND et al is just an 
> artifact of introducing those values for timeout 
> calculations/conversions - it could just be NANOS_PER_MILLI etc
> 
>> ------------------------------------------------------------------------------ 
>>
>> Regarding this from the audit:
>>
>> --- begin ---
>> ./share/gc/parallel/psParallelCompact.cpp: // os::javaTimeMillis() 
>> does not guarantee monotonicity.
>> ...
>> ./share/gc/shared/referenceProcessor.cpp: // os::javaTimeMillis() does 
>> not guarantee monotonicity.
>>
>> These are all describing why the subsequent code uses javaTimeNanos 
>> not javaTimeMillis.
>> --- end ---
>>
>> Do we really still support platforms that don't have a monotonic
>> clock?  I guess we appear to at least try.  But it's really wrong that
>> callers of os::javaTimeNanos should even think they need to cope with
>> that function being non-monotonic.
>>
>> Hm, I always thought System.nanoTime() was a monotonic clock, but I
>> don't see any such guarantee. So I guess Java just doesn't have such a
>> thing. Wow!
>>
>> So I guess none of this is really relevant to the change at hand after 
>> all.
> 
> I think you read the comments the wrong way round. The code uses 
> javaTimeNanos not javaTimeMillis because javaTimeMillis is not monotonic 
> and the code wants a monotonic clock. These comments were mostly 
> inserted when the incorrect use of javaTimeMillis was replaced with 
> javaTimeNanos.
> 
> Thanks,
> David
> -----
> 
>> ------------------------------------------------------------------------------ 
>>
>>
>>


More information about the hotspot-dev mailing list