black hole detection and concurrency

Sterling Clover s.clover at
Sat Dec 27 12:40:00 EST 2008

On Dec 27, 2008, at 9:02 AM, Bertram Felgenhauer wrote:
> The key part here is 'myThreadId >>= killThread' which throws an
> asynchronous exception to the thread itself, causing the update
> frames to be saved on the heap.
> Note that 'myThreadId >>= killThread' is not equivalent to
> 'throw ThreadKilled'; it is a synchronous exception and replaces  
> thunks
> pointed to by the update frames by another call to the raise  
> primitive -
> the result being that the exception gets rethrown whenever such a  
> thunk
> is evaluated. This happens with 'finally' and 'bracket': they use
> 'throw' for re-throwing the exception.
> See rts/RaiseAsync.c (raiseAsync() in particular) for the gory details
> for the first case, and rts/Schedule.c, raiseExceptionHelper() for the
> second case.
> In the above code, there is a small window between catching the
> ThreadKilled exception and throwing it again though, where other
> exceptions may creep in. The only way I see of fixing that is to use
> 'block' and 'unblock' directly.

That certainly seems to do the trick for the simple example at least.  
One way to reason about it better would be, instead of folding  
everything into the race function, to simply modify ghc's bracket  
function to give us the behavior we'd prefer (speaking of which, I  
recall there's something in the works for 6.12 or so to improve  
rethrowing of asynchronous exceptions?)

brackAsync before after thing =
   block (do
     a <- before
     r <- catch
            (unblock (thing a))
            (\_ -> after a >> myThreadId >>= killThread >> brackAsync  
before after thing )
     after a
     return r
     where threadKilled ThreadKilled = Just ()
           threadKilled _            = Nothing

This brackAsync just drops in to the previous code where bracket was  
and appears to perform correctly. Further, if we place a trace after  
the killThread, we se it gets executed once when the example is read  
(i.e. a resumption) but it does not get executed if the (`seq` v) is  
removed from the example So this gives me some hope that this is  
actually doing what we'd like. I don't doubt it may have further  
kinks however.


More information about the Glasgow-haskell-users mailing list