| | 1 | = The I/O Manager = |
| | 2 | |
| | 3 | This page describes the internals of the I/O manager, the latest version of which can be found in [http://darcs.haskell.org/libraries/base/GHC/Conc.lhs GHC.Conc], in the "Thread IO API" section. The I/O manager's job is to to provide a blocking I/O API to the user without forcing the RTS to create one operating system thread per Haskell thread. We here focus on the ''threaded'' RTS on non-Windows platforms. |
| | 4 | |
| | 5 | The RTS keeps a global list of pending events, unsuprising called `pendingEvents`, containing a elements of the following data type: |
| | 6 | |
| | 7 | {{{ |
| | 8 | data IOReq |
| | 9 | = Read {-# UNPACK #-} !Fd {-# UNPACK #-} !(MVar ()) |
| | 10 | | Write {-# UNPACK #-} !Fd {-# UNPACK #-} !(MVar ()) |
| | 11 | }}} |
| | 12 | |
| | 13 | When a thread wants to read from a file descriptor `fd` it calls `threadWaitRead` which in turn calls `waitForReadEvent`. |
| | 14 | |
| | 15 | {{{ |
| | 16 | waitForReadEvent :: Fd -> IO () |
| | 17 | waitForReadEvent fd = do |
| | 18 | m <- newEmptyMVar |
| | 19 | atomicModifyIORef pendingEvents (\xs -> (Read fd m : xs, ())) |
| | 20 | prodServiceThread |
| | 21 | takeMVar m |
| | 22 | }}} |
| | 23 | |
| | 24 | `waitForReadEvent` creates a new `MVar`, adds it to `pendingEvents` and finally blocks on it. `pendingEvents` gets read by the I/O manager thread which runs the event loop, in GHC called `service_loop`. It roughly performs these steps: |
| | 25 | |
| | 26 | 1. Pick up new I/O requests from `pendingRequests` and set the variable to the empty list. |
| | 27 | 2. Create data structures appropriate for calling `select`. |
| | 28 | 3. For each `Read` request in `pendingEvents` check if the file descriptor is in the ready set returned by `select`. If so perform a `putMVar` on the `MVar` associated with that request to wake up the blocked thread. |
| | 29 | 4. Repeat from step 1. |