[Haskell-cafe] Waiting on input with `hWaitForInput' or `threadWaitRead'

Ertugrul Soeylemez es at ertes.de
Tue Oct 18 12:21:16 CEST 2011

Jason Dusek <jason.dusek at gmail.com> wrote:

> > I don't think you want either of the functions you mentioned.  What
> > you probably want instead is to do concurrent programming by
> > creating Haskell threads.  A hundred Haskell threads reading from
> > Handles are translated to one or more OS threads using whatever
> > polling mechanism (select(), poll(), epoll) your operating system
> > supports.
> >
> > I have uploaded a simple concurrent echo server implementation to
> > hpaste [1]. It uses one thread for the stdout logger, one thread for
> > the server, one thread for each client and finally a main thread
> > waiting for you to hit enter to quit the application.
> >
> > [1] http://hpaste.org/52742 - Concurrent echo server with logger
> I am not sure how to apply the principle you mention to a proxy, which
> must read from and write to both handles in turn (or, ideally, as
> needed).

A proxy server acts a lot like an echo server.  The difference is that
usually before the actual proxying starts you have a negotiation phase,
and instead of echoing back to the same socket, you just write it to a
different one.  Here is an (untested) example:

    (clientH, clientHost, clientPort) <- accept serverSock
    destH <- negotiate clientH
    doneVar <- newEmptyMVar

    forkIO (hGetContents clientH >>= hPutStr destH >>= putMVar doneVar)
    forkIO (hGetContents destH >>= hPutStr clientH >>= putMVar doneVar)
    replicateM_ 2 (takeMVar doneVar)
    mapM_ hClose [clientH, destH]

Of course this code is going to bite you in production for two reasons:
First of all it has no error handling.  If the 'negotiate' function
throws an exception, then nobody will close the client handle.  So view
this is a highly simplified example!

The second reason is that in this lazy I/O framework it is
extraordinarily difficult to write the 'negotiate' function in the first
place, unless you allow yourself to put stuff back into the handle or
process only one byte at a time.  Both options are bad.  A better option
is to use a proper I/O abstraction suitable for protocol processing.
Iteratees [1] come to mind.  They solve this problem elegantly and let
you really just use the parser style "destH <- negotiate".

My usage of the MVar is actually kind of an abuse.  I just use it to
allow the two forwarder threads to signal their completion.  The main
thread just waits for the two to complete and then closes both handles.
The word "abuse" is perhaps too strong, because there is essentially
nothing wrong with the approach.  The standard concurrency library
doesn't provide an event primitive, so the more general MVar is often
used for this.

[1] http://www.yesodweb.com/book/enumerator


nightmare = unsafePerformIO (getWrongWife >>= sex)

More information about the Haskell-Cafe mailing list