[GHC] #7353: Make system IO interruptible on Windows
GHC
cvs-ghc at haskell.org
Mon Nov 19 13:13:36 CET 2012
#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
More information about the Glasgow-haskell-bugs
mailing list