[Haskell-cafe] GHC, odd concurrency space leak

Simon Peyton-Jones simonpj at microsoft.com
Sat Apr 17 08:41:28 EDT 2010


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?

If you can boil down the leak to a simple test case, do submit a Trac ticket.

Simon

From: haskell-cafe-bounces at haskell.org [mailto:haskell-cafe-bounces at haskell.org] On Behalf Of Jason Dagit
Sent: 14 April 2010 22:50
To: Gregory Collins
Cc: Haskell Cafe
Subject: Re: [Haskell-cafe] GHC, odd concurrency space leak


On Wed, Apr 14, 2010 at 2:44 PM, Jason Dagit <dagit at codersbase.com<mailto:dagit at codersbase.com>> wrote:

On Wed, Apr 14, 2010 at 2:13 PM, Gregory Collins <greg at gregorycollins.net<mailto:greg at gregorycollins.net>> wrote:
Jesper Louis Andersen <jesper.louis.andersen at gmail.com<mailto:jesper.louis.andersen at gmail.com>> writes:

> This post describes some odd behaviour I have seen in GHC 6.12.1 when writing
> Combinatorrent. The post is literate Haskell so you can run it. The executive
> summary: A space leak occurs when a new process is spawned from inside another
> process - and I can't figure out why. I am asking for help on haskell-cafe.
>
> ...[snip]...
>>
>> import Control.Monad.State

Does the problem go away if you use "Control.Monad.State.Strict"?

Nope :)  That was the first thing I tried here.

I tried playing with optimization level too.

Next I tried making two versions that were as similar as possible and then comparing the core with ghc-core.  I can't see a difference between a version that uses 1MB and a version that uses 160MB (on my system 160MB is the worst I can get it to blow up).

The two versions I compared:
Low memory:
\begin{code}
> startp4 :: IO ThreadId
> startp4 = spawn () () (return ())

> startp3 :: IO ThreadId
> startp3 = spawn () () (forever $
>                        do liftIO startp4
>                           liftIO $ putStrLn "Delaying"
>                           liftIO $ threadDelay (3 * 1000000))

> main1 = do
>   putStrLn "Main thread starting"
>   startp3
>   threadDelay (1 * 1000000)
>
> main = main1
\end{code}

Too much memory:
\begin{code}
> startp4 :: IO ThreadId
> startp4 = spawn () () (forever $ return ())

> startp3 :: IO ThreadId
> startp3 = spawn () () (forever $
>                        do liftIO startp4
>                           liftIO $ putStrLn "Delaying"
>                           liftIO $ threadDelay (3 * 1000000))

> main1 = do
>   putStrLn "Main thread starting"
>   startp3
>   threadDelay (1 * 1000000)
>
> main = main1
\end{code}

The difference is whether or not the threads must keep returning () or if they returns it once.

I'm not sure what to make of it.  My conclusion is that keeping the thread alive via forever is the problem, but when I test this hypothesis with a threadDelay the space leak goes away:

\begin{code}
> startp4 :: IO ThreadId
> startp4 = spawn () () (liftIO $ threadDelay (100 * 1000000))

> startp3 :: IO ThreadId
> startp3 = spawn () () (forever $
>                        do liftIO startp4
>                           liftIO $ putStrLn "Delaying"
>                           liftIO $ threadDelay (3 * 1000000))

> main1 = do
>   putStrLn "Main thread starting"
>   startp3
>   threadDelay (1 * 1000000)
>
> main = main1
\end{code}

It will be interesting to hear what fixes this!

> forever' m = do _ <- m
>                 forever' m

When I define that version of forever, the space leak goes away.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://www.haskell.org/pipermail/haskell-cafe/attachments/20100417/9704ba4a/attachment.html


More information about the Haskell-Cafe mailing list