#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
Glasgow-haskell-bugs@haskell.org
http://www.haskell.org/mailman/listinfo/glasgow-haskell-bugs

Reply via email to