The Revenge of Finalizers

Simon Marlow simonmar at
Fri Oct 18 13:13:21 EDT 2002

> > probably we shouldn't get a crash, but a blackhole instead.
> Even a blackhole is wrong.  There's no cycle so it ought to evaluate
> successfully.

I didn't mean to suggest that a blackhole is correct, sorry for the
confusion.  No, obviously the code should just work.

> > Fixing it so that the evaluation of x is actually continued is what
> > we want, but I can't see an easy way to do that.
> I think GHC does the right thing (semantically) here.  From memory,
> GHC works like this:
>   on entering thunk: turn it from a thunk into a blocking queue
>    (optimization: delay the transformation until context switch time)
>   on entering a blocking queue:
>    if cyclic structure (i.e., already under evaluation by 
> same thread),
>      report blackhole.
>    otherwise, put this thread to sleep on the blocking queue.

Delete the sentence "if cyclic structure..." and that's correct.  If a
thread goes to sleep on its own blocking queue, then it just deadlocks,
and at some point later it will get a NonTermination exception when the
RTS realises what it has done.

> Based on this, what does GHC do here when a finalizer tries to
> evaluate a thunk already being evaluated?  The finalizer is put to
> sleep until the main thread finishes evaluation of the thunk.


> And what does GHC do if a call to unsafePerformIO hits a thunk already
> under evaluation by the same thread?  This is a blackhole (i.e.,
> deadlock).  The thread throws the blackhole exception.


> I think Hugs should report a blackhole in the unsafePerformIO case
> (I believe it does already).

Unless you try to exit and re-enter the RTS inside unsafePerformIO...
(admittedly not very likely, but I bet you could make it fall over this

> Hugs (and NHC, I believe) have no way to block finalizers once they
> start executing.
> I think our only choice is to turn off blackholing, allow the shared
> term to be evaluated twice over and hope that the two updates to the
> thunk (one by finalizer and then one by the main thread) don't do any
> harm.  I don't know if this would work.  Even if it does, it's not too
> palatable because of the loss of sharing and the space leaks (that
> blackholing normally fixes).

Yes, I don't think this is a good solution.

> > I suppose you could suspend either the finalizer or the main thread
> > using the trick of saving its stack on the heap - is this
> > implemented in Hugs?
> No - it is an STG-specific trick.  Hugs is based on the G-machine.
> In Hugs, the state of a pure evaluation is stored on the C stack and
> in the heap.  We could try longjmp-ing out of the finalizer and hope
> that the heap has a full and consistent record of the state but I'm
> not overly confident that this would work.  (Hmmm, longjmp-ing out is
> what exception handlers do and we believe that those work....)
> > The only other solution I can think of is to delay finalizers until
> > we get back up to the IO monad.  
> and not inside an unsafePerformIO call.



More information about the FFI mailing list