I/O multiplexing (repost)

Peter Simons simons at cryp.to
Mon Oct 27 18:06:10 EST 2003

[ I'm posting this article here again, because the general ]
[ mailing list seems to be closed to non-members, and I'm  ]
[ reading/posting through gmane.org. Pardon me, if you see ]
[ this on both lists, please.                  -peter      ]


I have a question concerning "manual" I/O multiplexing in Haskell, or
specifically with GHC. I have written an interface to the C library
ADNS, which performs asynchronous DNS queries. Everything is fine and
dandy, but now comes the hard part:

The function

    foreign import ccall adns_beforepoll
        :: State                -- ADNS context
        -> Ptr Pollfd           -- space for 'n' Pollfd
        -> Ptr CInt             -- n
        -> Ptr CInt             -- max timeout
        -> Ptr (Timeval)        -- current time
        -> IO CInt

generates an array of file descriptors, suitable for calling poll(2).
The function

    foreign import ccall adns_afterpoll
        :: State
        -> Ptr Pollfd
        -> CInt
        -> Ptr (Timeval)
        -> IO CInt

scans this array (after poll has been called), performs I/O on all
readable/writable sockets, and processes any packets that have been

Obviously, the library wants me to call poll myself, though. But since
poll is not supported by Haskell's run-time system, the call would
block _all_ running threads in my process, not just the thread doing
the DNS resolving.

I have considered the following options to solve this:

 1. Run the DNS resolver in a forkOS thread.

 2. Declare all imported functions to be thread-safe (what they are)
    and use forkIO.

 3. Call poll(2) without blocking, then conjure some magic to decide
    when to call it again without busy polling.

 4. Register the file descriptors with GHC's internal poll event loop.

Option (1) seems to be straight-forward, but I don't want to rely on
the non-standard forkOS function unless I really have to. The same
goes for (4): This is probably a good solution, but it's not portable
among compilers. (3) works fine, but is unsatisfactory because the
resolver will have comparably large latency times. (I have been
processing DNS queries every, say, second.)

So option (2) seems quite attractive compared to the others ... But
the question is: Would that even work?

Any ideas, thoughts, recommendations?


More information about the Haskell-Cafe mailing list