#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:  7415              |    Blocking:                             
     Related:                    |  
---------------------------------+------------------------------------------

Comment(by joeyadams):

 On Fri, Nov 16, 2012 at 5:34 AM, Simon Marlow wrote:
 > I suppose what I'm mainly concerned about is whether we're building in a
 requirement to do an OS-thread context switch for every IO request, which
 would be quite expensive.  That seems to be part of the current design,
 but I admit I still don't fully understand the details.  (I'll take
 another look at the code now).

 You're right, each IO request does involve context switches.  Here's how a
 typical IO request proceeds:

  * Application thread sends work to IO manager thread, then waits on an
 MVar

  * IO manager thread dequeues a completion with this work, and executes
 it.

  * IO manager thread later dequeues a completion signifying that the work
 completed, then fills the MVar so the application thread can proceed.

 My IO manager runs in a bound thread, unlike the one in GHC.Event.  This
 means there are two context switches per operation.  On my system, each
 request takes about 30 microseconds (for comparison, a system call to
 gettimeofday takes about 1 microsecond).

 GHC.Event will probably do better than my IO manager for lots of little
 sequential operations in a single thread.  But for lots of IO running
 concurrently, my IO manager should have decent performance per operation,
 provided threads are scheduled like this:

  * Thread posts work to IO manager, and waits on MVar.  RTS schedules
 another thread without switching OS threads.  This thread also posts work
 to IO manager, and so on.

  * IO manager thread picks up several work requests and executes them, all
 without a context switch.

  * IO manager thread picks up several completions and fills the
 corresponding MVars, again without a context switch.

  * RTS reschedules application threads one by one.

 If we want better performance for sequential operations, we'll have to do
 something more clever.  One idea might be to introduce a new scheduling
 primitive:

 {{{
 -- | Allow the RTS to schedule unbound threads to the current
 -- operating system thread for the given number of microseconds.
 -- This may only be called from a bound thread.
 donateTimeSlice :: Int -> IO ()

 -- | Wake up a call to 'donateTimeSlice' issued by a bound thread.
 endTimeSlice :: ThreadId -> IO ()
 }}}

 This way, when the IO manager detects no pending IO operations, it donates
 time to the thread pool, rather than blocking on
 `GetQueuedCompletionStatus`.  When the application sends work to the IO
 manager, it calls endTimeSlice so the IO manager can wake up and check for
 completion packets.

 Ideally, application code will spend most of its time in donated time
 slices, so the RTS can schedule the IO manager without context switching.

 In any case, the IO manager needs control of what OS thread it runs on,
 since both `GetQueuedCompletionStatus` and overlapped I/O system calls are
 sensitive to the current thread.

 > One thing you do have to be careful of is that the RTS needs to be able
 to start and stop the IO manager itself; see win32/ThrIOManager.c. Perhaps
 you're not planning to integrate the existing IO manager with yours, but
 in that case we'll have two IO manager threads with different purposes -
 is your IO manager handling threadDelay?

 Yes, my IO manager will handle threadDelay.  The existing implementation
 in GHC.Conc.Windows works OK (can be interrupted), but does not scale
 well, at least in theory.  It uses insertion sort to keep timeouts in
 order.  My IO manager, like GHC.Event, uses a priority search queue
 (GHC.Event.PSQ) to track timeouts.

 We might have to keep the old IO manager around to handle console events.
 I'm not sure if you can wait for that using overlapped I/O or not.

 Stopping the IO manager might be problematic.  According to MSDN, we can't
 close the completion port HANDLE until all references to it (HANDLEs
 associated with it) are closed first.  Why does the RTS need to start and
 stop the IO manager?  When does this happen?

 I pushed a branch named windows-iocp to the base repo.  It adds the new IO
 manager, and uses it to implement threadDelay and registerDelay.

-- 
Ticket URL: <http://hackage.haskell.org/trac/ghc/ticket/7353#comment:10>
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