The Revenge of Finalizers
simonmar at microsoft.com
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
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