<br><br><div class="gmail_quote">On Wed, Apr 14, 2010 at 2:44 PM, Jason Dagit <span dir="ltr">&lt;<a href="mailto:dagit@codersbase.com">dagit@codersbase.com</a>&gt;</span> wrote:<br><blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;">
<br><br><div class="gmail_quote"><div class="im">On Wed, Apr 14, 2010 at 2:13 PM, Gregory Collins <span dir="ltr">&lt;<a href="mailto:greg@gregorycollins.net" target="_blank">greg@gregorycollins.net</a>&gt;</span> wrote:<br>
<blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;">
<div>Jesper Louis Andersen &lt;<a href="mailto:jesper.louis.andersen@gmail.com" target="_blank">jesper.louis.andersen@gmail.com</a>&gt; writes:<br>
<br>
&gt; This post describes some odd behaviour I have seen in GHC 6.12.1 when writing<br>
&gt; Combinatorrent. The post is literate Haskell so you can run it. The executive<br>
&gt; summary: A space leak occurs when a new process is spawned from inside another<br>
&gt; process - and I can&#39;t figure out why. I am asking for help on haskell-cafe.<br>
&gt;<br>
</div>&gt; ...[snip]...<br>
&gt;&gt;<br>
&gt;&gt; import Control.Monad.State<br>
<br>
Does the problem go away if you use &quot;Control.Monad.State.Strict&quot;? <br></blockquote></div><div><br>Nope :)  That was the first thing I tried here.<br><br>I tried playing with optimization level too.<br><br>Next I tried making two versions that were as similar as possible and then comparing the core with ghc-core.  I can&#39;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).<br>

<br>The two versions I compared:<br>Low memory:<br>\begin{code}<br>&gt; startp4 :: IO ThreadId<br>&gt; startp4 = spawn () () (return ())<br><br>&gt; startp3 :: IO ThreadId<br>&gt; startp3 = spawn () () (forever $<br>&gt;                        do liftIO startp4<br>

&gt;                           liftIO $ putStrLn &quot;Delaying&quot;<br>&gt;                           liftIO $ threadDelay (3 * 1000000))<br><br>&gt; main1 = do<br>&gt;   putStrLn &quot;Main thread starting&quot;<br>&gt;   startp3<br>

&gt;   threadDelay (1 * 1000000)<br>&gt;<br>&gt; main = main1<br>\end{code}<br><br>Too much memory:<br>\begin{code}<br>&gt; startp4 :: IO ThreadId<br>&gt; startp4 = spawn () () (forever $ return ())<br><br>&gt; startp3 :: IO ThreadId<br>

&gt; startp3 = spawn () () (forever $<br>&gt;                        do liftIO startp4<br>&gt;                           liftIO $ putStrLn &quot;Delaying&quot;<br>&gt;                           liftIO $ threadDelay (3 * 1000000))<br>

<br>&gt; main1 = do<br>&gt;   putStrLn &quot;Main thread starting&quot;<br>&gt;   startp3<br>&gt;   threadDelay (1 * 1000000)<br>&gt;<br>&gt; main = main1<br>\end{code}<br><br>The difference is whether or not the threads must keep returning () or if they returns it once.<br>

<br>I&#39;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:<br><br>\begin{code}<br>&gt; startp4 :: IO ThreadId<br>

&gt; startp4 = spawn () () (liftIO $ threadDelay (100 * 1000000))<br><br>&gt; startp3 :: IO ThreadId<br>&gt; startp3 = spawn () () (forever $<br>&gt;                        do liftIO startp4<br>&gt;                           liftIO $ putStrLn &quot;Delaying&quot;<br>

&gt;                           liftIO $ threadDelay (3 * 1000000))<br><br>&gt; main1 = do<br>&gt;   putStrLn &quot;Main thread starting&quot;<br>&gt;   startp3<br>&gt;   threadDelay (1 * 1000000)<br>&gt;<br>&gt; main = main1<br>

\end{code}<br><br>It will be interesting to hear what fixes this!<br></div></div></blockquote><div><br>&gt; forever&#39; m = do _ &lt;- m<br>&gt;                 forever&#39; m<br><br>When I define that version of forever, the space leak goes away.<br>
</div></div>