#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