GHC does not use select()/poll() correctly on non-Linux platforms
From my discovery at https://phabricator.haskell.org/D42#30542:
Why does the existing code work on platforms that are not Linux? In my select man page it says: On Linux, select() modifies timeout to reflect the amount of time not slept; most other implementations do not do this. (POSIX.1-2001 per‐ mits either behavior.) This causes problems both when Linux code which reads timeout is ported to other operating systems, and when code is ported to Linux that reuses a struct timeval for multiple select()s in a loop without reinitializing it. Consider timeout to be undefined after select() returns. The existing select loop seems to rely on the fact that &tv is updated as described here.
man 2 poll.
man 2 select on FreeBSD 11 says explicitly:
BUGS Version 2 of the Single UNIX Specification (``SUSv2'') allows systems to modify the original timeout in place. Thus, it is unwise to assume that the timeout value will be unmodified by the select() system call. FreeBSD does not modify the return value, which can cause problems for applications ported from other systems.
I have tested this now on FreeBSD, and indeed it doesn't work as expected.
With GHC 7.10.2:
import System.IO main = hWaitForInput stdin (1 * 1000)
ghc --make test.hs -rtsopts
[root@ ~]# time ./test real 0m1.386s user 0m0.004s sys 0m0.000s [root@ ~]# time ./test +RTS -V0.01 real 0m1.386s user 0m0.001s sys 0m0.000s [root@ ~]# time ./test +RTS -V0.001 real 0m1.678s user 0m0.003s sys 0m0.002s [root@ ~]# time ./test +RTS -V0.0001 real 0m11.311s user 0m0.032s sys 0m0.139s
See how when we increase the timer signal, the sleep suddenly takes 10x longer than it should.
That's because it triggers the case where EINTR is received in https://github.com/ghc/ghc/blob/f46369b8a1bf90a3bdc30f2b566c3a7e03672518%5E/libraries/base/cbits/inputReady.c\#L48, letting us use the same unmodified 1-second
struct timeval *timeout again and again.
This demo of the bug works for GHC 7.10 and 8.0.1; in 8.0.2
hWaitForInput is broken (https://ghc.haskell.org/trac/ghc/ticket/12912\#[ticket:13497\#comment:134309](https://gitlab.haskell.org//ghc/ghc/issues/13497#note_134309)) so the demo doesn't work there.
Convenience: Here is the call chain of hWaitForInput