[Haskell-cafe] Timeouts that don't cause data growth.

David Leimbach leimy2k at gmail.com
Tue Mar 23 16:06:53 EDT 2010


On Tue, Mar 23, 2010 at 1:02 PM, Bas van Dijk <v.dijk.bas at gmail.com> wrote:

> On Tue, Mar 23, 2010 at 8:23 PM, David Leimbach <leimy2k at gmail.com> wrote:
> > Is this just a problem of spawning too many forkIO resources that never
> > produce a result?
>
> It looks like it. Lets look at the implementation of timeout:
>
> timeout :: Int -> IO a -> IO (Maybe a)
> timeout n f
>    | n <  0    = fmap Just f
>    | n == 0    = return Nothing
>    | otherwise = do
>        pid <- myThreadId
>        ex  <- fmap Timeout newUnique
>        handleJust (\e -> if e == ex then Just () else Nothing)
>                   (\_ -> return Nothing)
>                   (bracket (forkIO (threadDelay n >> throwTo pid ex))
>                            (killThread)
>                            (\_ -> fmap Just f))
>
> We see a thread is forked that throws the Timeout exception to the
> current thread after n microseconds. However when the current thread
> finishes early this timeout thread will be killed. I assume that when
> a thread is killed it can be garbage collected. (However we have to
> watch out for [1]) So it's a big surprise to me that we're seeing this
> space-leak!
>
> Maybe you can file a bug report?
>

Seems like I should


>
> > I was thinking of trying something like the following in System.Timeout's
> > place:
> >> module Main where
> >> import Control.Concurrent.MVar
> >> import Control.Concurrent
> >> import Data.Maybe
> >
> >> timeout :: Int -> IO a -> IO (Maybe a)
> >> timeout time action = do
> >>   someMVar <- newEmptyMVar   -- MVar is a Maybe
> >>   timeoutThread <- forkIO $ nothingIzer time someMVar
> >>   forkIO $ actionRunner action someMVar timeoutThread
> >>   takeMVar someMVar >>= return
> >>     where
> >>       nothingIzer time mvar = threadDelay time >> putMVar mvar Nothing
> >>       actionRunner action mvar timeoutThread = do
> >>                         res <- action
> >>                         killThread timeoutThread
> >>                        putMVar mvar $ Just res
> >> main :: IO ()
> >> main = do
> >>  res <- timeout (5 * 10 ^ 6) (getLine >>= putStrLn)
> >>  case res of
> >>     Nothing -> putStrLn "Timeout"
> >>     Just x -> putStrLn "Success"
>
> The original timeout obeys the following specification:
>
> "The design of this combinator was guided by the objective that
> timeout n f  should behave exactly the same as f as long as f doesn't
> time out. This means that f has the same myThreadId  it would have
> without the timeout wrapper. Any exceptions f might throw cancel the
> timeout and propagate further up. It also possible for f to receive
> exceptions thrown to it by another thread."
>
> They implement this by executing the action in the current thread.
> Yours executes the action in another thread.
>

True, but mine's not leaking space! ;-)  I think I can fix the action
running in the other thread issue.


>
> regards,
>
> Bas
>
> [1]
> http://haskell.org/ghc/docs/latest/html/libraries/base-4.2.0.0/Control-Concurrent.html#t%3AThreadId
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://www.haskell.org/pipermail/haskell-cafe/attachments/20100323/fc7fb831/attachment.html


More information about the Haskell-Cafe mailing list