threadDelay is disrupted by system clock changes
threadDelay is sensitive to changes to the system clock. Two behaviors are observed:
- When the system clock is moved forward, threadDelay will timeout prematurely, because it thinks that amount of time elapsed.
- When the system clock is moved backward, threadDelay will take longer to complete, because it's waiting for the system clock to catch up.
Whether these behaviors are present depends on both the operating system and the use of -threaded. Here are the configurations I tested:
- ghc-7.4.1 Linux 64-bit: Disrupted by both forward and backward clock changes
- ghc-7.4.1 Linux 64-bit, -threaded: Disrupted only by backward clock changes
- ghc-7.2.2 Windows 32-bit: Not disrupted by clock changes (behaves correctly)
- ghc-7.2.2 Windows 32-bit, -threaded: Disrupted only by backward clock changes
To reproduce the problem, first compile the attached program. It uses System.Posix.Clock from the "clock" package to get the time, unaffected by system clock changes. Then run it. It will produce output like this:
0: 0s 1: 10s
Set the system clock forward by a minute. On Linux without -threaded, it will print something like this:
5: 50s 6: 52s
6 is printed 2 seconds after 5, because threadDelay didn't wait a full 10 seconds like it should have.
Now set the system clock backward by a minute. On Windows with -threaded, or on Linux with or without -threaded, it will print something like this:
3: 30s 4: 101s
4 is printed over a minute after 3, because threadDelay waited for the system clock to recover the time subtracted by changing the time.
For convenience, here's a way to fix the system clock on Linux after fiddling around with it:
sudo ntpdate pool.ntp.org
Digging through the source, each configuration seems to boil down to the following system calls:
- Linux: gettimeofday (via getourtimeofday in posix/Itimer.c)
- Linux, -threaded: gettimeofday, epoll_wait
- Windows: Sleep
- Windows, -threaded: GetSystemTimeAsFileTime, WaitForSingleObject
Perhaps the calls to gettimeofday and GetSystemTimeAsFileTime should be replaced with monotonic time functions (i.e. ones not affected by system clock changes):
- Linux, FreeBSD: http://stackoverflow.com/q/211055/149391
- Mac OS X: http://stackoverflow.com/a/6725161/149391
- Windows: http://stackoverflow.com/q/211257/149391