The above seems to be because in rts/Schedule.c only resumeThread() is called, and schedule() (or whatever function's task it is to choose functions to run on our single capability in the non-threaded runtime) is not called.

Added a test for 'timeout' to be accurate.
This is the first in a series of regression tests prompted by
https://ghc.haskell.org/trac/ghc/ticket/8684 and D4011, D4012, D4041
Test Plan: This _is_ a test.
Reviewers: nh2, austin, hvr, bgamari
Reviewed By: bgamari
Subscribers: rwbarton, thomie
GHC Trac Issues: #8684
Differential Revision: https://phabricator.haskell.org/D4074

Remove the 'legroom' part of the timeout-accurate-pure test.
Summary:
This removes the part of the test that checks whether the timeout happened in
a 'reasonable' amount of time, because it is flaky.
In subsequent work, we can turn this into a benchmark.
Test Plan: This _is_ a test
Reviewers: nh2, bgamari, Phyx, austin, hvr
Reviewed By: Phyx
Subscribers: rwbarton, thomie
GHC Trac Issues: #8684
Differential Revision: https://phabricator.haskell.org/D4120

﻿Note that the thread calling CancelSynchronousIo doesn't really know where the thread that called the synchronous operation is. The thread could have been pre-empted and it has yet to actually communicate with the device; it could be suspended, waiting for the device to respond; or the device could have just responded, and the thread is in the process of returning from its call. If CancelSynchronousIo is called when the specified thread is not actually suspended waiting for the device to respond, CancelSynchronousIo returns FALSE and GetLastError returns error not found.

I managed to make it work on Windows for FILE_TYPE_CHAR, by using WaitForMultipleObjects() and passing it 2 objects: The HANDLE we're interested in, an Event object that we signal when we want to interrupt the thread.

After searching for a very long time, I ended up with the above solution.

Here's the backstory of me finding it out via the #ghc and #winapi IRC channels:

In #ghc:

<nh2[m]> JaffaCake_: may I steal your attention for a minute? `ccall interruptible` doesn't seem to work for me on Windows, it claims that there's nothing to cancel, do you have an idea what this could be? https://ghc.haskell.org/trac/ghc/ticket/8684#comment:25
<JaffaCake_> no idea, sorry
<JaffaCake_> there was a bug reported recently in process that also looks like foreign ccall interruptible not working on windows
<nh2[m]> JaffaCake_: yes, I'm aware of it and have talked a bit with Neil about it, I'll try to make that interruptible after I've made hWaitForInput interruptible (already works on Linux -threaded for me), but currently it seems interruptible doesn't work at all for me on Windows
<JaffaCake_> TBH I don't remember whether it ever worked
<nh2[m]> JaffaCake_: there's another thing that I've already looked into a couple hours but haven't found a way yet:
<nh2[m]> My non-Windows fix works nicely on -threaded, but for nonthreaded it doesn't, because the scheduler makes it immediately re-enter the foreign call, so the `throwTo` in `timeout` doesn't get a chance to run.
<nh2[m]> Would it be possible to say, in the scheduler: Whenever a thread returns from a foreign call, yield, so that other Haskell threads have a chance to run?
<JaffaCake_> I think that's probably a bad idea
<JaffaCake_> if the timeout has fired, then there should be a blocked exception
<JaffaCake_> and that should get thrown immediately the FFI call returns
<nh2[m]> hWaitForInput/fdReady() gets woken up by the SIGVTALRM timer signal, and then the scheduler immediately runs fdReady() again
<nh2[m]> so `timeout` never actually gets a chance to throwTo the exception
<nh2[m]> so in other words, the timeout can never fire
<JaffaCake_> nh2[m], you're not running with -threaded?
<nh2[m]> JaffaCake_: no, that's what I meant, it's working fine with -threaded on Linux, but not with non-threaded. This problem (and my question on whether we could yield after FFI return) is only for non-threaded
<JaffaCake_> oh, I'm not worried about non-threaded
<nh2[m]> JaffaCake_: but I am :D I'm trying to make it work equally well across both threaded and non-threaded; in general I care a lot about non-threaded as for some applications it is significantly faster, and it is also easier to debug
<JaffaCake_> but you'll need to add hacks to make it work, hacks that could add overhead for -threaded
<nh2[m]> JaffaCake_: my hope is that such overhead would be negligible, as it would occur only for `interruptible` syscalls (which by nature are expensive, so a bit of bookkeeping around them should not matter much). For example, all calls to `fdReady()` that are performance-sensitive, nonblocking (timeout=0), already use `unsafe` and thus wouldn't suffer any overhead
<nh2[m]> having non-threaded not working would be quite a drag for us, as we found it to perform way better for sequential-IO-heavy (e.g. network) code
<nh2[m]> and with things like `timeout` not working for `hWaitForInput`, we can't put timeouts on reads or writes in high-level the code (would have to handle such logic at the very low level of hWaitForInput itself)
<nh2[m]> e.g. `timeout N $ seriesOfVariousNetworkProtocolActions`

In #winapi:

nh2
18:54 hi, I'm trying to WaitForSingleObject(GetStdHandle(STD_INPUT_HANDLE), ...) on stdin, and to cancel this waiting using CancelSynchronousIo(), but the cancellation does nothing (returns 0 and GetLastError() is ERROR_NOT_FOUND). Any idea what I could be doing wrong?
SleepyISIS (IRC)
19:29 nh2[m]: If this function cannot find a request to cancel, the return value is 0 (zero), and GetLastError returns ERROR_NOT_FOUND.
19:30 Are you sure that you want to cancel IO, not the wait itself?
nh2
19:31 SSleepyISIS: can you elaborate a bit on that distinction? To me those sound like the same things
19:33 what I need to do is use a function that blocks until input is available on the given HANDLE to a FILE_TYPE_CHAR, up to a maximum amount of time (e.g. in milliseconds), but if some other event occurs, I need to cancel that blocking wait
John___ (IRC)
19:33 nh2[m], there's SleepEx()
19:34 which does what you want.
19:35 also WaitForMultiple/SingleObject()
19:35 depends what your goal is.
nh2
19:35 JJohn___: that one seems to just wait a given amount of time, independent from input being available on some HANDLE. I want to wait until either the specified time has passed, or there's input available on the HANDLE, or the waiting has been cancelled (e.g. using CancelSynchronousIO)
SleepyISIS (IRC)
19:35 nh2[m]: Is terminating a thread an option?
John___ (IRC)
19:36 nh2[m], then WaitForSingleObject()
nh2
19:37 JJohn___: yes, that is what I am using, and what doesn't seem to work (as I wrote above)
SleepyISIS (IRC)
19:37 Or stick to asynch solution.
John___ (IRC)
19:37 it waits for a timeout or when input is available on a handle.
19:37 I was not here to see what the problem was. mind pasting it again?
nh2
19:37 SSleepyISIS: regarding terminating a thread, likely not, it should ideally work in a single-threaded use case (I'm doing this for an improvement in the GHC Haskell compiler), but I would still very much like to hear what you have in mind, maybe it is possible
SleepyISIS (IRC)
19:38 John___: If I got nh2[m] correctly, (s)he wants to abort synch IO before the wait timeouts.
nh2
19:38 JJohn___: I've just written up the problem more clearly on https://stackoverflow.com/questions/47336755/how-to-cancelsynchronousio-on-waitforsingleobject-waiting-on-stdin
How to CancelSynchronousIo() on WaitForSingleObject() waiting on stdin?
On Windows 10, I'm waiting for input from the console using WaitForSingleObject( GetStdHandle(STD_INPUT_HANDLE), ... ) and to cancel this waiting using CancelSynchronousIo(). But the cancellatio...
SleepyISIS (IRC)
19:39 nh2[m]: https://stackoverflow.com/questions/34372611/how-to-signal-file-handle-waiting-with-waitforsingleobject
How to signal file HANDLE waiting with WaitForSingleObject
This code, which I have no control over, reads a file using overlapped I/O: // Read file asynchronously HANDLE hFile = CreateFile(..., FILE_FLAG_OVERLAPPED, ...); BYTE buffer[10]; OVERLAPPED oRead...
19:39 Hmm, what value do you get from GetStdHandle?
John___ (IRC)
19:40 nh2[m], synchronous solutions cannot be "unblocked"
19:40 that's why you use async.
19:40 let me read the stack post first though before I reply differently.
nh2
19:42 JJohn___: it was my understanding so far that CancelSynchronousIo()'s purpose is to cancel specifically functions like WaitForSingleObject(), so I'm surprised that it doesn't work
John___ (IRC)
19:42 I don't think it's for those purposes.
nh2
19:42 SSleepyISIS: the HANDLE returned from GetStdHandle() is 0 for the stdin
SleepyISIS (IRC)
19:43 >My colleague suggested WaitForMultipleObjects() as a workaround. So we can wait on either the console/stdin handle or an event which signals termination.
19:43 Good solution.
19:43 0 doesn't seem to be a valif handle.
19:44 Yeah: If an application does not have associated standard handles, such as a service running on an interactive desktop, and has not redirected them, the return value is NULL.
19:44 https://docs.microsoft.com/en-us/windows/console/getstdhandle
John___ (IRC)
19:44 yeah, that ^
19:45 using that in combination with SetEvent()
19:45 allows you to either make WaitForMultipleObjects() to catch on WAIT_SIGNALED or WAIT_TIMEOUT
nh2
19:45 SSleepyISIS: oh sorry, I typoed my printf. The HANDLE returned from GetStdHandle() is 84 for the stdin
John___ (IRC)
19:45 or whatevet
19:45 so you can make it either trigger when the handle input is available
nh2
19:48 SSleepyISIS: JJohn___ OK, so I would get rid of the CancelSynchronousIo() completely, and always make sure that any WaitForSingleObject() on stdin is instead a WaitForMultipleObjects() on stdin AND the special separate event handle I use for "early termination" of the wait?
Mysoft (IRC)
19:50 nh2[m]
19:50 you tried to use CancelIoEx
19:50 isntead of CancelIoSynchronous?
nh2
19:55 MMysoft: I will try that now; what would you pass as the lpOverlapped argument, NULL?
Mysoft (IRC)
19:56 yeah
19:56 and which OS you are trying that?
19:56 and you're running from console?
19:56 or inside an IDE?
nh2
19:57 MMysoft: that call fails with GetLastError() == 6 (ERROR_INVALID_HANDLE). I'm on Windows 10, running from cmd.exe
Mysoft (IRC)
19:58 ok
19:58 so this was a behavior change
19:58 because here it works
19:58 i think it changes in some update on win7
19:58 (bug?)
19:59 there's a OldNewThing about the subject
19:59 https://blogs.msdn.microsoft.com/oldnewthing/20150323-00/?p=44413
20:00 but anyway using WaitForMultipleObjects is the way to go
20:00 as i think using CancelIoEx... looks as bad as "ungetc" :P
nh2
20:04 MMysoft: ah wait, I am using CancelIoEx() wrong. For the CancelSynchronousIo() I had to provide a thread handle as an argument, but for CancelIoEx() I have to provide the actual file handle as the argument. Let me retry
20:05
nh2
20:06 MMysoft: OK, with that fixed, CancelIoEx() gives me GetLastError() == ERROR_NOT_FOUND, like for CancelSynchronousIo()
20:07 I'll try the oldnewthing example code on my system
[Note by nh2: I didn't actually manage to make that compile immediately with msys, and it didn't turn out to be necessary to solve the issue]
John___ (IRC)
20:46 nh2[m], yes, you pass 2 handles and you check which got signaled.
20:46 the result is WAIT_OBJECT_0 to (WAIT_OBJECT_0 + nCount– 1)
nh2
00:11 JJohn__: SSleepyISIS MMysoft : thanks for your help, I just got the first working implementation for GHC that fixes its behaviour on Windows, using your proposed extra event + WaitForMultipleObjects() approach!

Back in #ghc:

<nh2[m]> JaffaCake: I think I have finally figured out how to fix the Windows problem with CancelSynchronousIo() having no effect. Some people on #winapi helped me, saying that this function might not work in practice and instead we could use WaitForMultipleObjects(), with 2 objects, one for the actual FD HANDLE and one Event we'd signal if we want WaitForMultipleObjects() to return early. I've coded that up and it works.
<nh2[m]> But for efficiency, I need one Event per thread, so that I don't wake up all threads in `interruptWorkerTask()`, but only the target one.
<nh2[m]> My plan is thus to have a `HANDLE interruptOSThreadEvent;` in the `struct Task`
<nh2[m]> But I need to access that in `inputReady.c`, which is in base, where I cannot get at the currently running task with `myTask()`
<nh2[m]> is there a way I can ask for the currently running Haskell task from that location?
<JaffaCake> I'm pretty sure we do that in integer-gmp
<nh2[m]> JaffaCake: I don't know how thread-local storage actually works internally, but I suppose it's done by symbol names somehow, and if I just declare `extern __thread Task *my_task;` in `inputReady.c`, it would refer to the right thing technically? (though I'd still have to bring `Task` into scope)
<JaffaCake> just expose a function from the RTS to return the thread-local Event
<JaffaCake> I was thinking of rts_unsafeGetMyCapability()
<JaffaCake> which is a similar kind of thing
<nh2[m]> JaffaCake: which would be the best header file to put it in? Probably somewhere under `includes/rts`?
<JaffaCake> next to rts_unsafeGetMyCapability()
<nh2[m]> JaffaCake: OK, and it would have to go somewhere in RtsSymbols.c, probably `RTS_MINGW_ONLY_SYMBOLS`, is that right?

As part of the review of my patch in ​https://phabricator.haskell.org/D42, I have gathered some information of how the timer signal is implemented. Since that may be a useful by itself, I post it here. It is as of commit a1950e6, and, since not much of this has changed since the last release, also of GHC 8.2.

A pthread is started that runs a loop reading from the timerfd.
No SIGVTALRM is used.
When the timerfd ticks, that thread wakes up and calls handle_tick().

pthread without timerfd

A pthread is started that runs a loop running sleep(itimer_interval).
No SIGVTALRM is used.
When that thread finishes the sleep, it calls handle_tick().

timer_create()

A SIGVTALRM signal handler is set up that handle_tick().
Then timer_create() is called to set up a SIGVTALRM signal occurring regularly,
using the ITIMER_REAL real-time clock.
The SIGVTALRM signal occurring will EINTR all system calls of all threads of the process.

setitimer()

A SIGVTALRM signal handler is set up that handle_tick().
Then setitimer() is called to set up a SIGVTALRM signal occurring regularly, using the CLOCK_ID clock, which is CLOCK_MONOTONIC if available and CLOCK_REALTIME otherwise.
The SIGVTALRM signal occurring will EINTR all system calls of all threads of the process.

CreateTimerQueueTimer()

CreateTimerQueueTimer() is set up to call tick_callback() which calls tick_proc = handle_tick() regularly.
The option WT_EXECUTEINTIMERTHREAD is passed which results in "callback function is invoked by the timer thread itself".
There are a couple issues with it:

The period is set to TimeToUS(tick_interval) / 1000 milliseconds, which becomes 0 if less than a millisecond is chosen. CreateTimerQueueTimer() does not document what happens if a 0-period is given. It might busy-poll, but it's not documented, so who knows?

A comment in the code remarks that this timer has a maximum accuracy of 15ms on Windows 7, and even worse on older platforms.

@simonmar Oops, you are right. I swapped threaded and non-threaded when typing down my table, thus writing we use pthreads in non-threaded, which is exactly the wrong way around. I'll edit to reflect that.