I&#39;m looking for information about black hole detection with ghc.&nbsp; I&#39;m getting &quot;&lt;&lt;loop&gt;&gt;&quot; where I don&#39;t think there is an actual black hole.&nbsp; I get this message sometimes with the unamb package, which is implemented with unsafePerformIO, concurrency, and killThread, as described in <a href="http://conal.net/blog/posts/functional-concurrency-with-unambiguous-choice/">http://conal.net/blog/posts/functional-concurrency-with-unambiguous-choice/</a> and <a href="http://conal.net/blog/posts/smarter-termination-for-thread-racing/">http://conal.net/blog/posts/smarter-termination-for-thread-racing/</a> .<br>
<br>Suppose I have a definition &#39;v = unsafePerformIO ...&#39;, and v is used more than once.&nbsp;&nbsp; Evaluation (to whnf) of v is begun and the evaluation thread gets killed before evaluation is complete.&nbsp; Then the second use begins.&nbsp; Will the second evaluation be (incorrectly) flagged as a black hole?<br>
<br>I haven&#39;t found a simple, reproducible example of incorrect black-hole reporting.&nbsp; My current examples are tied up with the Reactive library.&nbsp; I do have another strange symptom, which is &quot;thread killed&quot; message.&nbsp; I wonder if it&#39;s related to the &lt;&lt;loop&gt;&gt; message.&nbsp; Code below.<br>
<br>&nbsp;&nbsp;&nbsp; Thanks,&nbsp; - Conal<br><br><br>import Prelude hiding (catch)<br>import System.IO.Unsafe<br>import Control.Concurrent<br>import Control.Exception<br><br><br>-- *** Exception: thread killed<br>main :: IO ()<br>main = print $ f (f True) where f v = (v `unamb` True) `seq` v<br>
<br>-- | Unambiguous choice operator.&nbsp; Equivalent to the ambiguous choice<br>-- operator, but with arguments restricted to be equal where not bottom,<br>-- so that the choice doesn&#39;t matter.&nbsp; See also &#39;amb&#39;.<br>
unamb :: a -&gt; a -&gt; a<br>unamb a b = unsafePerformIO (evaluate a `race` evaluate b)<br><br>-- | Race two actions against each other in separate threads, and pick<br>-- whichever finishes first.&nbsp; See also &#39;amb&#39;.<br>
race :: IO a -&gt; IO a -&gt; IO a<br>race a b = do<br>&nbsp;&nbsp;&nbsp; v &lt;- newEmptyMVar<br>&nbsp;&nbsp;&nbsp; let t x = x &gt;&gt;= putMVar v<br>&nbsp;&nbsp;&nbsp; withThread (t a) $ withThread (t b) $ takeMVar v<br>&nbsp;where<br>&nbsp;&nbsp; withThread u v = bracket (forkIO u) killThread (const v)<br>
<br>