The GHC IO manager uses epoll / kqueue under the hood without any special programmer effort. Just write a naive threaded program that puts each simultaneous blocking IO call into a separate thread - and the GHC will make sure that it works the way you want.
Daniel Wagner
source share