#7353: Make system IO interruptible on Windows
---------------------------------+------------------------------------------
Reporter: joeyadams | Owner:
Type: bug | Status: new
Priority: normal | Milestone: 7.8.1
Component: libraries/base | Version: 7.6.1
Keywords: | Os: Windows
Architecture: Unknown/Multiple | Failure: Incorrect result at runtime
Difficulty: Unknown | Testcase:
Blockedby: | Blocking:
Related: |
---------------------------------+------------------------------------------
Comment(by joeyadams):
''maybe foreign import ccall interruptible will work for the FFI calls in
the network package.''
Unfortunately, this is only available on Windows Vista and up. My program
has to run on Windows XP.
I looked into some potential approaches to wait for IO on Windows. Please
do point out any errors in this assessment.
== Completion ports ==
This would involve a manager thread that repeatedly calls
[http://msdn.microsoft.com/en-
us/library/windows/desktop/aa364986(v=vs.85).aspx
GetQueuedCompletionStatus]. To perform sends and receives, we would use
calls like [http://msdn.microsoft.com/en-
us/library/windows/desktop/ms737606(v=vs.85).aspx ConnectEx] and
[http://msdn.microsoft.com/en-
us/library/windows/desktop/ms741688(v=vs.85).aspx WSARecv]. Caveats:
* IOCP doesn't provide a way to wait for socket readiness, as far as I
can tell. This means threadWaitRead and IODevice.ready will have to be
emulated by some other means.
On the other hand, it might be possible using zero-size reads/writes.
* IO operations are sensitive to the calling thread. From the
documentation of [http://msdn.microsoft.com/en-
us/library/windows/desktop/ms741688(v=vs.85).aspx WSARecv]:
'''Note''' All I/O initiated by a given thread is canceled when that
thread exits. For overlapped sockets, pending asynchronous operations can
fail if the thread is closed before the operations complete. See
!ExitThread for more information.
Thus, we'll probably need a manager that assigns I/O jobs to threads such
that no thread has multiple pending jobs involving the same HANDLE.
== select ==
We could have a thread call [http://msdn.microsoft.com/en-
us/library/windows/desktop/ms740141(v=vs.85).aspx select] to wait on
sockets in bulk. Caveats:
* select() is limited to 64 sockets, so we'd have to manage a pool of
threads to wait for more sockets.
* As far as I can tell, there is no way to interrupt select() except by
giving it a short timeout, or by writing to a control socket to prod the
IO manager (the GHC event manager does this). We can't create such a
socket on Windows without making the program host on a system port.
I suppose we could repeat the select() every 0.1 seconds or so, but this
would cause a lot of latency; each read and write would spend up to this
long waiting for the IO manager.
A faster approach would be to have the caller do a blocking select for a
short period of time. If that times out, then we use the IO manager.
This keeps quick waits quick, and has little effect on longer waits.
== WSAEventSelect ==
We could instead use [http://msdn.microsoft.com/en-
us/library/windows/desktop/ms741576(v=vs.85).aspx WSAEventSelect] and
[http://msdn.microsoft.com/en-
us/library/windows/desktop/ms687025(v=vs.85).aspx WaitForMultipleObjects],
which provides more flexibility than select(), and lets us create our own
event handle which we can use to interrupt the IO manager. Caveats:
* WSAEventSelect sets the socket to non-blocking mode, and cancels any
previous WSAEventSelect and WSAAsyncSelect calls on the same socket. This
might clash with libraries.
* !WaitForMultipleObjects is also limited to 64 handles, so we'd have to
manage a thread pool.
== A plan ==
Here's a plan: implement an IO manager for Windows using
!WaitForMultipleObjects, which allows callers to wait on their own HANDLEs
using a function like this:
{{{
registerHandle :: EventManager -> (HandleKey -> IO ()) -> HANDLE -> IO
HandleKey
}}}
Using WSAEventSelect, we can implement the following on top:
{{{
evtRead, evtWrite, evtOOB, evtAccept, evtConnect, ... :: Event
registerSocket :: EventManager -> (SocketKey -> Event -> IO ()) -> Fd ->
Event -> IO FdKey
}}}
This API is modeled off of
[http://hackage.haskell.org/packages/archive/base/latest/doc/html/GHC-
Event.html GHC.Event].
Now we can implement an alternative to threadWaitRead and threadWaitWrite
for Windows sockets:
{{{
waitSocket :: Event -> Fd -> IO ()
}}}
With this, it should be possible to update Network.Socket and GHC.IO.FD so
blocking operations can be interrupted without leaking OS threads.
Mission accomplished.
We could probably do better with IOCP, but I think that would be more
complex.
--
Ticket URL: <http://hackage.haskell.org/trac/ghc/ticket/7353#comment:2>
GHC <http://www.haskell.org/ghc/>
The Glasgow Haskell Compiler
_______________________________________________
Glasgow-haskell-bugs mailing list
[email protected]
http://www.haskell.org/mailman/listinfo/glasgow-haskell-bugs