Waiting on Sockets or File Descriptors

Hi, I have the following problem: I use an external library through the foreign function interface which gives me _several_ sockets and expects me to call it again when any of those sockets becomes readable or writable. Since I know that the Haskell run-time system has all the required functionality in place, I wonder whether there is any way to register these sockets in the internal scheduler and have it call an 'IO ()' function in case of such an event? What makes matters more complicated is that the _library_ owns these sockets -- not me. So I cannot really touch them or lift them into a 'Handle'. Any ideas? Peter

On Tue, Feb 01, 2005 at 03:16:44PM -0800, Peter Simons wrote:
Hi,
I have the following problem: I use an external library through the foreign function interface which gives me _several_ sockets and expects me to call it again when any of those sockets becomes readable or writable.
Since I know that the Haskell run-time system has all the required functionality in place, I wonder whether there is any way to register these sockets in the internal scheduler and have it call an 'IO ()' function in case of such an event?
What makes matters more complicated is that the _library_ owns these sockets -- not me. So I cannot really touch them or lift them into a 'Handle'.
Any ideas?
In GHC you can try with these functions from GHC.Conc: threadWaitRead :: Int -> IO () threadWaitWrite :: Int -> IO () Best regards, Tomasz

Tomasz Zielonka writes:
threadWaitRead :: Int -> IO () threadWaitWrite :: Int -> IO ()
Thanks for the pointer! Am I correct in assuming that there is no "more high-level" mechanism for scheduling more than one file descriptor? Like select(2) or poll(2) would do? I guess, I could implement it on top of those functions with some clever forkIO'ing, but I wonder whether that's particularly efficient? I'm asking because I am fairly certain that other people will run into this problem too. It would be nice to have a more generic mechanism for this purpose, particularly if it will (eventually) be supported by other implementations as well. I mean, all the necessary infrastructure is bound to be available in the run-time system anyway. Peter

Hi! On Wed, Feb 02, 2005 at 02:57:24PM +0100, Peter Simons wrote:
Tomasz Zielonka writes:
threadWaitRead :: Int -> IO () threadWaitWrite :: Int -> IO ()
Thanks for the pointer!
Am I correct in assuming that there is no "more high-level" mechanism for scheduling more than one file descriptor? Like select(2) or poll(2) would do?
Well, I *guess* that this is exactly what GHC will do internally. And I seem to dimly remember a what-is-more-high-level-discussion here, but it may have been on a different topic ;-) Just do an strace, if you want to know (and are running Linux). Take care, Carsten -- Carsten Schultz (2:38, 33:47) http://carsten.codimi.de/ PGP/GPG key on the pgp.net key servers, fingerprint on my home page.

On Wed, Feb 02, 2005 at 05:58:58AM -0800, Peter Simons wrote:
Tomasz Zielonka writes:
threadWaitRead :: Int -> IO () threadWaitWrite :: Int -> IO ()
Thanks for the pointer!
Am I correct in assuming that there is no "more high-level" mechanism for scheduling more than one file descriptor?
This is the most high-level mechanism I can imagine.
Like select(2) or poll(2) would do?
You seem to what something low-level.
I guess, I could implement it on top of those functions with some clever forkIO'ing, but I wonder whether that's particularly efficient?
This translates to select() (or poll()), but yes, you'll pay some price for creating and scheduling Haskell threads. Best regards, Tomasz

Tomasz Zielonka writes:
Like select(2) or poll(2) would do?
You seem to what something low-level.
Well, my point is that I don't really want to deal with the file descriptors. What I'd really like to do is to register a call-back function; similar to the way signal handlers work. I don't want to wait for something to happen -- I'd like to be notified when something happens. By the way, the source code for the module says: -- Note: threadDelay, threadWaitRead and threadWaitWrite -- aren't really functional on Win32, but left in there -- because lib code (still) uses them (the manner in which -- they're used doesn't cause problems on a Win32 platform -- though.) So I guess I can't really use them in code that's supposed to be portable among different platforms? Maybe 'forkOS' combined with calling poll() through FFI really is the best solution? I seem to recall reading somewhere that the threaded RTS was more efficient for these applications anyway? Peter

So I guess I can't really use them in code that's supposed to be portable among different platforms?
Maybe 'forkOS' combined with calling poll() through FFI really is the best solution? I seem to recall reading somewhere that the threaded RTS was more efficient for these applications anyway?
Two minor points: a) poll() is not supported on Mac OS X and (at least some popular versions of) BSD. b) 'forkIO' in the threaded RTS would suffice in this case, as the poll() or select() system calls don't use any thread-local state. In the threaded RTS, "safe" foreign imports never affect other threads, and you only need forkOS if the actual identity of the OS thread matters (i.e. when using thread-local state, OpenGL, some GUI libraries). Apart from that, my guess is that it would be fairly efficient with the threaded RTS. The only inefficiency would be that if you're doing regular IO to "slow" file handles at the same time, you'd get one (OS) thread in GHC's libraries calling select() for the regular fds, plus a separate (OS) thread running your call to select()/poll(). I'm not sure if cleaning this up would be worth the effort - you get at least two OS threads anyway. Cheers, Wolfgang

Wolfgang Thaller writes:
a) poll() is not supported on Mac OS X and (at least some popular versions of) BSD.
Are you certain? Just tried "man poll" on one of the MacOS X machines the SourceForge compile farm offers, and that one had it: "Darwin ppc-osx1 5.5 Darwin Kernel Version 5.5".
b) 'forkIO' in the threaded RTS would suffice in this case, as the poll() or select() system calls don't use any thread-local state. In the threaded RTS, "safe" foreign imports never affect other threads [...].
That would be really good news! I assumed that GHC's runtime system used one thread for _all_ FFI invocations? (Unless you start new ones.) So I thought calling poll() would block all other FFI invocations until it returned? Or is that only for "unsafe" FFI calls? Do you have an URL for me where I can find out more about this, by any chance? Peter
participants (4)
-
Carsten Schultz
-
Peter Simons
-
Tomasz Zielonka
-
Wolfgang Thaller