13

There's a desktop application that uses Thread.sleep() to achieve long (minutes or hours) delays. This same application has been working fine from Windows XP through (at least) Windows 7. The application calculates how far in the future it needs to do something, then hits a Thread.sleep(msToWait). This has been working fine, 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. It appears that the machine begins executing code at "msToWait" plus the time the machine has been in S3 (not 100% sure of this right now, but likely).

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

Testing has been on the current JVM 1.7.

Is this a Windows 10 bug? Is this a JVM bug? Is there a work-around?

ADDITIONAL DATA:

A test program and procedure were developed. The procedure is to run the program, cause the machine to sleep for about a minute, then wake the machine and wait for the program to finish.

If this the program is run on Windows 10 (reporting as 8) with JVM Version: 25.40-b25, it fails:

C:\Users\Tester\Downloads>SleepTester.exe
Wed Apr 01 10:47:35 PDT 2015 Using default number of minutes: 5
Wed Apr 01 10:47:35 PDT 2015 You can use "SleepTester -minutes 10" to have it sleep for 10 minutes, for example.
Wed Apr 01 10:47:35 PDT 2015 JVM Version: 25.40-b25 Windows Version: Windows 8
Wed Apr 01 10:47:35 PDT 2015 The program will now wait for 5 minutes.  Expect wrap-up at Wed Apr 01 10:52:35 PDT 2015
Wed Apr 01 10:53:38 PDT 2015 The system has come through the Thread.sleep(300000).
Wed Apr 01 10:53:38 PDT 2015 This should be a low number: 63589
Wed Apr 01 10:53:38 PDT 2015 This appears to be operating incorrectly...the expected sleep time has NOT been achieved.
Wed Apr 01 10:53:38 PDT 2015 Program is ending.

If the process is run on Windows 7, it does not fail.

Wed Apr 01 17:12:18 EDT 2015 Java Runtime Version: 1.8.0_31-b13 JVM Version: 25.31-b07 Windows Version: Windows 7
Wed Apr 01 17:12:18 EDT 2015 The program will now wait for 6 minutes.  Expect wrap-up at Wed Apr 01 17:18:18 EDT 2015
Wed Apr 01 17:18:18 EDT 2015 The system has come through the Thread.sleep(360000). 
Wed Apr 01 17:18:18 EDT 2015 This should be a low number: 0
Wed Apr 01 17:18:18 EDT 2015 Program is ending.

This is the test program:

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.");
}
}

I realize I could try various other methods to sleep, but I thought that since I went through and documented this, I'd post it here before trying other things.

Additional Information: This failure seems also to appear in Windows 8 (but not 7 or prior).

ADDITION 4/4/2019

The issue is visible on bugs.java.com at the following url [JDK-8221971][1].

There are a few earlier bugs linked to that bug. A comment from the linke JDK-8146730 bug:

17-04-2017 Any news on this topic?

04-04-2019 It has been deferred. It is a low priority, and complex, issue no one is actively assigned to it.

ADDITION 2/17/2021

This is probably due to a change in the way the Windows operating system has been programmed to respond to timeouts. Even using Windows API directly, I'm unsure how to accomplish the goal of making old and new Windows OS's behave the same.

Windows XP, Windows Server 2003, Windows Vista, Windows 7, Windows Server 2008 and Windows Server 2008 R2: The dwMilliseconds value does include time spent in low-power states. For example, the timeout does keep counting down while the computer is asleep.

Windows 8, Windows Server 2012, Windows 8.1, Windows Server 2012 R2, Windows 10 and Windows Server 2016: The dwMilliseconds value does not include time spent in low-power states. For example, the timeout does not keep counting down while the computer is asleep.

https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-waitforsingleobjectex

Dale
  • 4,421
  • 2
  • 37
  • 68
  • Not sure where the bug is, but can you try and use `TimeUnit.XXX.sleep(someQuantity)` instead, or `Thread.nanoSleep()` (the formers are just wrappers for the latter) and see if it makes a difference? Also, what JVM? – fge Apr 01 '15 at 15:04
  • I would suggest to experiment with different JVM versions (maybe it makes a difference, if the JVM version you are using ... is older or newer than Windows 10). Then there is a very reliable way to determine if its a JVM bug: go and write a defect against Oracle and see what happens ;-) – GhostCat Apr 01 '15 at 15:06
  • 1
    It must also be worth seeing if a `ScheduledExecutorService` (created via `Executors.newScheduledThreadPool(1);` and then submitting a job to it with [`schedule`](http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ScheduledExecutorService.html#schedule-java.lang.Runnable-long-java.util.concurrent.TimeUnit-) has the same issue. – Powerlord Apr 01 '15 at 15:16
  • Thanks guys, I've added "JVM 1.7" to the question. Good idea on making a defect against Oracle. – Dale Apr 01 '15 at 15:16
  • 1
    @Dale Still might be a good idea to test it with Java 8u40. – Powerlord Apr 01 '15 at 15:17
  • I have added to the question again, and that includes tests with Java 1.8 on both Windows 7 (works) and Windows 10 (fails). – Dale Apr 01 '15 at 21:48
  • 1
    I don't know why it worked before but I think the way is behave is expected. Anyway, it's not the good way to do it on windows. You should use Waitable Timers, with absolute time (relative time will behave exactly as Sleep). They exist since Windows XP: https://docs.microsoft.com/en-us/windows/desktop/sync/waitable-timer-objects (they can even wake up the machine if it when to sleep and it's timeout, if the machine is configured for that). I can provide a C++ example but I have no idea how to use these from Java :-) – Simon Mourier Apr 01 '19 at 12:56
  • Thanks Simon. Access to Windows Waitable Timer from Java requires JNI, I think, so kind of a pain, but worth considering. I found this https://stackoverflow.com/questions/53787825/is-there-java-thread-interrupt-asynchronous-queue-and-put-in-alertable – Dale Apr 02 '19 at 19:27
  • The link is specifically about async. You just need to call the SetWaitableTimer Windows API w/o async (APC proc argument), or use java's timer for that matter. – Simon Mourier Apr 04 '19 at 07:29
  • @Powerlord Good idea with `ScheduleExecutorService`, but that suffers from the same problem as Thread.sleep(). – Dale Feb 14 '21 at 18:42

2 Answers2

6

This is expected, valid behavior. The documentation is very explicit, stating:

these sleep times are not guaranteed to be precise, because they are limited by the facilities provided by the underlying OS.

and:

In any case, you cannot assume that invoking sleep will suspend the thread for precisely the time period specified.

nobody
  • 19,010
  • 17
  • 53
  • 73
  • It is NOT expected considering older versions of Windows did not 'stop time' during S3. I am not asking for precision, which is what is being addressed in both of those snips. All I'm asking about is why the behavior is markedly different for the new version of Windows. – Dale Apr 02 '15 at 15:16
  • The referenced documentation says nothing about the underlying OS's suspend states, which is central to the question here. – Dale Apr 02 '15 at 15:21
  • 2
    The spec says precision is not guaranteed, therefore you may not expect precision. The fact that one underlying OS happened to provide precision does not create an obligation on Java to maintain that precision forever in the future. Java never promised precision. – nobody Apr 02 '15 at 21:53
  • We have a difference of opinion about precision. Apparently if a sleep time is inaccurate by _hours_ (or more), you consider that a question of precision. I do not. – Dale Apr 16 '15 at 15:11
  • @Dale _"All I'm asking about is why the behavior is markedly different for the new version of Windows."_ That sounds more like a question for [SuperUser](https://superuser.com/), doesn't it? – walen Apr 03 '19 at 11:07
  • @walen I think it's a programming question because a user of a program that behaved differently in Win7 vs Win10 would just see the program doing the wrong thing and wouldn't have a clue why. The programmer can see the line of code that's acting differently between the two OS's. – Dale Apr 03 '19 at 15:29
  • @Dale What I mean is: *now* that you know (thanks to current answers and comments) that the Java spec says `sleep()` behavior depends on the underlying OS, and that the different behavior is not because of a change in the Java spec or the JVM... *then* asking why the behavior of programs coming back from S3 sleep mode is different in different versions of Windows seems like a question that might get a more proper answer in SuperUser than here. – walen Apr 03 '19 at 15:52
3

it's probably a bug in windows 10.. even os vital things done in metro like the start menu sometimes fail to wake up from suspended state in a timely fashion(depending of what all you have firewalled or disabled, unsurprisingly enough). I would have suggested checking the state of the thread/process with processhacker or some sysinternals tools and try to come up with some solution. then check with windows event logs and such.

or just do something silly like replace the sleep with a semaphore and sleep that gets executed in the command line.

the docs are pretty sure imho that it should wake up in a timely fashion, just that you shouldn't rely on it as an output timer on some datalines, playing music or such, but taking 100x the time is a little different.

Lassi Kinnunen
  • 381
  • 3
  • 9