JDK-8221971 : Java Thread.sleep() on Windows 10 stops in S3 sleep status
  • Type: Enhancement
  • Component: hotspot
  • Sub-Component: runtime
  • Affected Version: 11,12,13
  • Priority: P3
  • Status: Closed
  • Resolution: Won't Fix
  • OS: windows_10
  • CPU: x86_64
  • Submitted: 2019-03-31
  • Updated: 2019-04-09
  • Resolved: 2019-04-09
Related Reports
Relates :  
Relates :  
Description
ADDITIONAL SYSTEM INFORMATION :
All Window 10, current and earlier JVM's (it's been this way for at least four years).

A DESCRIPTION OF THE PROBLEM :
Given a desktop application that uses Thread.sleep() to achieve long (minutes or hours) delays, the application calculates how far in the future it needs to do something, then hits a Thread.sleep(msToWait). This has been working fine before Windows 8, even if the system happens to go into S3 sleep state during the wait.

As of Windows 10, though, the code after Thread.sleep() does not execute "on time" if the machine has been in S3 during the interval. It appears that the machine begins executing code at "msToWait" plus the time the machine has been in S3.

Earlier versions of Windows did not exhibit this behavior; code after Thread.sleep() waited the right amount of time, irrespective of sleep status.


STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
A test program and procedure were developed. The procedure is to 1) run the program, 2) cause the machine to sleep for about a minute, then 3) wake the machine and wait for the program to finish.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The Thread.sleep() will sleep for approximately the amount of ms specified.
ACTUAL -
The Thread.sleep() sleeps for the amount of ms specified plus the amount of time the machine was in S3 or other suspended state.

---------- BEGIN SOURCE ----------
import java.util.Date;

public class SleepTester {

private static int mMinutes;
private static int mDefault = 5;

public static void main(String[] args) throws Exception {
    for (int iArg = 0; iArg < args.length; ++iArg) {
        if (args[iArg].equals("-minutes") && (iArg + 1) < args.length) {
            mMinutes = Integer.parseInt(args[++iArg]);
        }
    }

    if (mMinutes == 0) {
        mMinutes = mDefault;
        System.out.println(new Date() + " Using default number of minutes: " + mDefault);
        System.out.println(new Date() + " You can use \"SleepTester -minutes 10\" to have it sleep for 10 minutes, for example.");
    }

    System.out.println(new Date() + " Java Runtime Version: " + System.getProperty("java.runtime.version") + " JVM Version: " + System.getProperty("java.vm.version") + " Windows Version: " + System.getProperty("os.name"));
    long msDelay = mMinutes * 60 * 1000;
    long wakePoint = new Date().getTime() + msDelay;
    System.out.println(new Date() + " The program will now wait for " + mMinutes + " minutes.  Expect wrap-up at " + new Date(wakePoint));
    Thread.sleep(msDelay); // If the machine goes into S3 during this interval, it should not matter, as long as it's awake when it fires.
    System.out.println(new Date() + " The system has come through the Thread.sleep(" + msDelay + "). ");
    long msAccuracy = Math.abs(new Date().getTime() - wakePoint);
    System.out.println(new Date() + " This should be a low number: " + msAccuracy);
    if (msAccuracy > 1000) System.out.println(new Date() + " This appears to be operating incorrectly...the expected sleep time has NOT been achieved.");
    System.out.println(new Date() + " Program is ending.");
}
}
---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
Use the technique of the executable test case in a loop that checks for inaccuracies in the clock, and if found, reset the clock.

FREQUENCY : always



Comments
Based on comments from David Holmes, we can not fix this issue.
09-04-2019

In this particular case with things "working" in Windows 8 but not in Windows 10, this is likely due to a change in the timing hardware used by Windows. In 8 after the suspend it would cause timed-waits to fire, and in 10 it doesn't. I think you will find that Microsoft considered the behaviour in 8 to be a bug in general. While you can argue that "my program wants to wake up in 35 seconds no matter what" in general timed waits are used to delay one thread (or process) to give "enough time" for another thread/process to do something. If the machine is suspended then that other thread/process has not had the time it needs and so you actually want the timed-wait/sleep to continue until the specified amount of "executable machine time" has elapsed.
04-04-2019

The JVM does not work across machine suspension/sleep/hibernation. If a thread is blocked in a sleep or other timed call when the machine sleeps/suspends/hibernates then the thread remains blocked when the machine resumes until such time as the operating system unblocks the thread. The VM has no control over this and is not informed of any such gaps in "time". Blocking operations using absolute times may work correctly if they can truly be implemented as an OS-level operation that uses absolute time (which is not often the case). See also discussions in JDK-8146527, JDK-8146730. Whilst there is some scope for absolute time APIs that work across machine suspension, it is not generally the case that you want (IMHO) relative waits to observe that time passed during the suspension.
04-04-2019

Please evalulate whether Hotspot can be aware of gaps in time and suggest any workarounds. A coarse technique might be to limit the requested wait time to some smaller number and recompute periodically. However, that would increase the overhead by polling for time. If the VM was aware of sleep related changes, it might be able to return from thread.sleep earlier than expected and Thread.sleep could recompute the remaining time.
04-04-2019

Tested on Windows 10 (version 1803) with the following JDK versions after sleep of approximately a minute : JDK 11.0.2 - Fail JDK12+33(GA) - Fail JDK13-ea+14 - Fail Output on JDK 13-ea : D:\Shared>D:\JDK\jdk-13\bin\java SleepTester -minutes 3 Thu Apr 04 11:04:21 IST 2019 Java Runtime Version: 13-ea+14 JVM Version: 13-ea+14 Windows Version: Windows 10 Thu Apr 04 11:04:21 IST 2019 The program will now wait for 3 minutes. Expect wrap-up at Thu Apr 04 11:07:21 IST 2019 Thu Apr 04 11:08:18 IST 2019 The system has come through the Thread.sleep(180000). Thu Apr 04 11:08:18 IST 2019 This should be a low number: 57706 Thu Apr 04 11:08:18 IST 2019 This appears to be operating incorrectly...the expected sleep time has NOT been achieved. Thu Apr 04 11:08:18 IST 2019 Program is ending. Output on JDK-12: D:\Shared>D:\JDK\jdk-12\bin\java SleepTester -minutes 5 Thu Apr 04 10:55:51 IST 2019 Java Runtime Version: 12+33 JVM Version: 12+33 Windows Version: Windows 10 Thu Apr 04 10:55:51 IST 2019 The program will now wait for 5 minutes. Expect wrap-up at Thu Apr 04 11:00:51 IST 2019 Thu Apr 04 11:01:53 IST 2019 The system has come through the Thread.sleep(300000). Thu Apr 04 11:01:53 IST 2019 This should be a low number: 62036 Thu Apr 04 11:01:53 IST 2019 This appears to be operating incorrectly...the expected sleep time has NOT been achieved. Thu Apr 04 11:01:53 IST 2019 Program is ending.
04-04-2019