How can I poll files, sockets or handles to become readable/writable in Haskell?
Asked Answered
B

2

10

How could I watch several files/sockets from Haskell and wait for these to become readable/writable?

Is there anything like the select/epoll/... in Haskell? Or I am forced to spawn one thread per file/socket and always use the blocking resource from within that thread?

Brachiate answered 31/7, 2012 at 16:13 Comment(0)
F
16

The question is wrong: you aren't forced to spawn one thread per file/socket and use blocking calls, you get to spawn one thread per file/socket and use blocking calls. This is the cleanest solution (in any language); the only reason to avoid it in other languages is that it's a bit inefficient there. GHC's threads are cheap enough, however, that it is not inefficient in Haskell. (Additionally, behind the scenes, GHC's IO manager uses an epoll-alike to wake up threads as appropriate.)

Fawkes answered 31/7, 2012 at 16:27 Comment(5)
Thanks for the clarification :-) I was also thinking from languages like Erlang, where you spawn a light process and it is quite common to receive data from several sockets in the same process.Brachiate
Then how do you block on a file descriptor until something happens? Just saying "threads" isn't really answering the question.Laney
@Laney With e.g. hGetChar or hPutChar.Fawkes
That sort of works, but I just want to be notified of an action on the descriptor, not actually read data from it. If I use hGetChar to retrieve data from it, I get an exception "resource exhausted (Resource temporarily unavailable)" when I subsequently run the function that's actually supposed to read the data from it.Laney
Turns out there's threadWaitRead and threadWaitWrite in Control.Concurrent to block the current thread until a file descriptor is ready for reading / writing, without actually doing reading or writing.Laney
L
4

There's a wrapper for select(2): https://hackage.haskell.org/package/select
Example usage here: https://github.com/pxqr/udev/blob/master/examples/monitor.hs#L36

There's a wrapper for poll(2): https://hackage.haskell.org/package/poll

GHC base comes with functionality that wraps epoll on Linux (and equivalent on other platforms) in the GHC.Event module.
Example usage:

import GHC.Event
import Data.Maybe (fromMaybe)
import Control.Concurrent (threadDelay)

main = do
  fd <- getSomeFileDescriptorOfInterest
  mgr <- fromMaybe (error "Must be compiled with -threaded") <$> getSystemEventManager
  registerFd mgr (\fdkey event -> print event) fd evtRead OneShot
  threadDelay 100000000

More documentation at http://hackage.haskell.org/package/base-4.11.1.0/docs/GHC-Event.html

Example use of an older version of the lib at https://wiki.haskell.org/Simple_Servers#Epoll-based_event_callbacks Though, the loop in that example has since been moved to the hidden module GHC.Event.Manager, and is not exported publicly as far as I can tell. GHC.Event itself says "This module should be considered GHC internal."

In Control.Concurrent there's threadWaitRead and threadWaitWrite. So, to translate the above epoll example:

import Control.Concurrent (threadWaitRead)

main = do
  fd <- getSomeFileDescriptorOfInterest
  threadWaitRead fd
  putStrLn "Got a read ready event"

You can wrap the threadWaitRead and subsequent IO action in Control.Monad.forever to run them repeatedly. You can also wrap the thing in forkIO to run it in the background while your program does something else.

Laney answered 17/5, 2018 at 5:15 Comment(3)
Turns out there's threadWaitRead and threadWaitWrite in Control.Concurrent to block the current thread until a file descriptor is ready for reading / writing, without actually doing reading or writing.Laney
select is POSIX function, is it possible to use this package select in Windows? Haskell is MinGW based, so I hope it could work, but I am not sure...Retention
I would just use Control.Concurrent.threadWaitRead - which is already part of GHC, and should be cross-platform. I would not recommend use of that select library.Laney

© 2022 - 2024 — McMap. All rights reserved.