[Haskell-cafe] GHC, odd concurrency space leak

Bertram Felgenhauer bertram.felgenhauer at googlemail.com
Sat Apr 17 16:11:05 EDT 2010


Daniel Fischer wrote:
> Am Samstag 17 April 2010 14:41:28 schrieb Simon Peyton-Jones:
> > I have not been following the details of this, I'm afraid, but I notice 
> this:
> > > forever' m = do _ <- m
> > >                 forever' m
> >
> > When I define that version of forever, the space leak goes away.
> >
> > What was the old version of forever that led to the leak?
> 
> Control.Monad.forever
> 
> forever :: Monad m => m a -> m b
> forever m = m >> forever m
> 
> However, that isn't the problem. In my tests, both variants of forever 
> exhibit the same behaviour, what makes it leak or not is the optimisation 
> level.

This definition, plus sharing, is the source of the space leak.
Consider this modification of your code:

    import Control.Concurrent
    
    always :: Monad m => m a -> m b
    always a = -- let act = a >> act in act
        do
        _ <- a
        always a
    
    noop :: IO ()
    noop = return ()
    
    body :: IO ()
    body = always noop
    
    spawner :: IO ()
    spawner = do
        forkIO $ body
        putStrLn "Delaying"
        threadDelay 1000000
        body `seq` return ()
    
    main :: IO ()
    main = do
        putStrLn "Spawning"
        forkIO spawner
        putStrLn "Delaying main"
        threadDelay 4000000

Note that the 'always' in 'spawner' is gone, but it still exhibits the
space leak. The leak goes away if the final line of 'spawner' is removed,
hinting at the real problem: 'always' actually creates a long chain of
actions instead of tying the knot.

Indeed the following definition of 'always' (or 'forever') fares better
in that regard, but is more susceptible to producing unproductive loops:

    always a = let act = a >> act in act

(I used  noop = yield  for avoiding that problem in my tests)

regards,

Bertram


More information about the Haskell-Cafe mailing list