Proposal: System.Timeout module for base (Trac #980)

I have a comment on one of the comments (or perhaps a question): -- Foreign function calls, for example, cannot be timed out with this -- combinator simply because an arbitrary C function cannot receive -- asynchronous exceptions. When @timeout@ is used to wrap an FFI call that -- blocks, no timeout event can be delivered until the FFI call returns, which -- pretty much negates the purpose of the combinator. In practice, however, -- this limitation is less severe than it may sound. Standard I\/O functions -- like 'System.IO.hGetBuf', 'System.IO.hPutBuf', 'Network.Socket.accept', or -- 'System.IO.hWaitForInput' appear to be blocking, but they really don't -- because the runtime system uses scheduling mechanisms like @select(2)@ to -- perform asynchronous I\/O, so it is possible to interrupt standard socket -- I\/O or file I\/O using this combinator. In my experience, when run with -threaded, Network.Socket.accept does block, and async exceptions are not delivered (I'm using GHC-6.6 on Win XP). Is this what I should expect, or is it more likely that I've configured something (like my socket) incorrectly so that it blocks? Alistair ***************************************************************** Confidentiality Note: The information contained in this message, and any attachments, may contain confidential and/or privileged material. It is intended solely for the person(s) or entity to which it is addressed. Any review, retransmission, dissemination, or taking of any action in reliance upon this information by persons or entities other than the intended recipient(s) is prohibited. If you received this in error, please contact the sender and delete the material from any computer. *****************************************************************

Bayley, Alistair writes:
In my experience, when run with -threaded, Network.Socket.accept does block, and async exceptions are not delivered (I'm using GHC-6.6 on Win XP).
Oh. That's bad news. I don't think the socket configuration has anything to do with it, this seems to be an RTS decision. The truth is, however, I really don't know. Can someone else shed some light on this matter? Peter

would this combinator help solve the following problem: I have two (or more) threads (created by forkIO) and each does an external ( System.Cmd.system ) call. When the first of these returns, the others should be killed. (when I try to kill the Haskell RTS threads, the external programs still seem to continue). -- -- Johannes Waldmann -- Tel/Fax (0341) 3076 6479/80 -- ---- http://www.imn.htwk-leipzig.de/~waldmann/ -------

From: libraries-bounces@haskell.org [mailto:libraries-bounces@haskell.org] On Behalf Of Johannes Waldmann
would this combinator help solve the following problem:
I have two (or more) threads (created by forkIO) and each does an external ( System.Cmd.system ) call. When the first of these returns, the others should be killed.
(when I try to kill the Haskell RTS threads, the external programs still seem to continue).
That doesn't seem possible with System.Cmd. You may want to look at System.Process instead, because that has a terminateProcess function. You could (for each thread): 1. start process with runCommand or runProcess 2. call waitForProcess; when it completes, kill the other thread(s) 3. wrap step 2 in a catch; in the handler, call terminateProcess, then rethrow (this should catch the AsyncException). e.g. proc <- runCommand "blah blah" catch ( do ec <- waitForProcess proc killThread otherThread(s) return ec -- do you care about the exit code? ) (\e -> do terminateProcess proc throwIO e ) I haven't tried this - any better ideas? Alistair ***************************************************************** Confidentiality Note: The information contained in this message, and any attachments, may contain confidential and/or privileged material. It is intended solely for the person(s) or entity to which it is addressed. Any review, retransmission, dissemination, or taking of any action in reliance upon this information by persons or entities other than the intended recipient(s) is prohibited. If you received this in error, please contact the sender and delete the material from any computer. *****************************************************************

2. call waitForProcess; when it completes, kill the other thread(s)
does this work? In GHC docs for killThread/throwTo it says:
If the target thread is currently making a foreign call, then the exception will not be raised (and hence throwTo will not return) until the call has completed.
-- -- Johannes Waldmann -- Tel/Fax (0341) 3076 6479/80 -- ---- http://www.imn.htwk-leipzig.de/~waldmann/ -------

2. call waitForProcess; when it completes, kill the other thread(s)
does this work? In GHC docs for killThread/throwTo it says:
If the target thread is currently making a foreign call, then the exception will not be raised (and hence throwTo will not return) until the call has completed.
Well, I haven't tried it, like I said. I guess it depends on whether waitForProcess is implemented with an FFI call. Note that this blocking behaviour (exception blocked until FFI call returns) only seems to be with -threaded; with the normal RTS, the exception is raised (although what happens to the OS thread is not clear; I guess it still runs). At least, that's my experience on Win XP with GHC-6.6. Alistair ***************************************************************** Confidentiality Note: The information contained in this message, and any attachments, may contain confidential and/or privileged material. It is intended solely for the person(s) or entity to which it is addressed. Any review, retransmission, dissemination, or taking of any action in reliance upon this information by persons or entities other than the intended recipient(s) is prohibited. If you received this in error, please contact the sender and delete the material from any computer. *****************************************************************

Johannes Waldmann writes:
I have two (or more) threads (created by forkIO) and each does an external ( System.Cmd.system ) call. When the first of these returns, the others should be killed.
Unfortunately, 'timeout' doesn't help solving that problem. What you are looking for is 'terminateProcess': import System.IO import System.Process import Control.Concurrent ( threadDelay ) run :: FilePath -> Maybe Handle -> Maybe Handle -> IO ProcessHandle run cmd hin hout = runProcess cmd [] Nothing Nothing hin hout Nothing main :: IO () main = do pid <- withFile "/dev/urandom" ReadMode $ \hin -> withFile "/dev/null" WriteMode $ \hout -> run "/usr/bin/cat" (Just hin) (Just hout) threadDelay (5*10^6) -- sleep 5 seconds terminateProcess pid -- send kill signal waitForProcess pid >>= print -- wait on termination A mechanism that terminates all external processes once one of them terminates is easily defined using 'Control.Exception.bracket'. You'll find that the code structure is quite similar to timeout: main' :: IO () main' = do let start = withFile "/dev/urandom" ReadMode $ \hin -> withFile "/dev/null" WriteMode $ \hout -> run "/usr/bin/cat" (Just hin) (Just hout) stop p = terminateProcess p >> waitForProcess p bracket start stop $ \_ -> bracket start stop $ \_ -> bracket start stop $ \_ -> bracket start stop $ \_ -> return () -- kill em all NOW I hope this helps. Peter

Bayley, Alistair writes:
In my experience, when run with -threaded, Network.Socket.accept does block, and async exceptions are not delivered (I'm using GHC-6.6 on Win XP).
Oh. That's bad news. I don't think the socket configuration has anything to do with it, this seems to be an RTS decision. The truth is, however, I really don't know. Can someone else shed some light on this matter?
Peter
Well, I have a fairly simple test case, if anyone wants to see it. Alistair ***************************************************************** Confidentiality Note: The information contained in this message, and any attachments, may contain confidential and/or privileged material. It is intended solely for the person(s) or entity to which it is addressed. Any review, retransmission, dissemination, or taking of any action in reliance upon this information by persons or entities other than the intended recipient(s) is prohibited. If you received this in error, please contact the sender and delete the material from any computer. *****************************************************************

Bayley, Alistair wrote:
Bayley, Alistair writes:
In my experience, when run with -threaded, Network.Socket.accept does block, and async exceptions are not delivered (I'm using GHC-6.6 on Win XP).
Oh. That's bad news. I don't think the socket configuration has anything to do with it, this seems to be an RTS decision. The truth is, however, I really don't know. Can someone else shed some light on this matter?
Peter
Well, I have a fairly simple test case, if anyone wants to see it.
This is a Windows-specific bug. In fact all I/O on Windows in the threaded RTS is currently done with blocking foreign calls, so you won't be able to kill any threads blocked on I/O I'm afraid. In the non-threaded RTS you can kill the Haskell thread, but the I/O will still happen (actually I think this is worse than the -threaded behaviour). We'd like to do all I/O using an I/O manager thread, as we do on Unix. If there are any Windows experts listening, this is a 1-2 day task that would have great benefit to Windows users. Cheers, Simon

This is a Windows-specific bug. In fact all I/O on Windows in the threaded RTS is currently done with blocking foreign calls, so you won't be able to kill any threads blocked on I/O I'm afraid. In the non-threaded RTS you can kill the Haskell thread, but the I/O will still happen (actually I think this is worse than the -threaded behaviour).
Damn my lame platform! Well, that's a relief: at least I have an explanation as to why it works that way. I was wondering how I would gracefully kill something waiting for a socket accept. I assume that when the process exits, all threads (OS or Haskell) are terminated. So if I use the normal RTS, and have the main thread exit, any thread waiting on a socket accept will be terminated too. That seems to be my experience, anyway. I'd offer to help but I'm no Windows expert, and 1-2 days effort would be a few weeks elapsed time for me. Alistair ***************************************************************** Confidentiality Note: The information contained in this message, and any attachments, may contain confidential and/or privileged material. It is intended solely for the person(s) or entity to which it is addressed. Any review, retransmission, dissemination, or taking of any action in reliance upon this information by persons or entities other than the intended recipient(s) is prohibited. If you received this in error, please contact the sender and delete the material from any computer. *****************************************************************
participants (4)
-
Bayley, Alistair
-
Johannes Waldmann
-
Peter Simons
-
Simon Marlow