> > > It's going to be really hard to implement ForeignPtr as
> specified.
> > > The problem is that invocation of the cleanup function
> is triggered
> > > by the garbage collector.
> >
> > Malcolm can comment on exactly how nhc98 handles the
> finalizers, but I
> > believe that there's a list of pending finalizers
> maintained by the RTS
> > which are run on re-entry to Haskell land.
>> That's the theory, yes. In practice, I've never been able to get it
> to work correctly, so the only finalisers that are guaranteed to run
> in nhc98's runtime system are ones written in C rather than Haskell.
> The underlying problem is when do you run a Haskell finaliser in a
> non-concurrent system? You can't run it when you discover it (in
> the middle of a GC). When the GC is called, the mutator is probably
> right in the middle of some reduction step. If I run the finalisers
> immediately the GC finishes, then I'll probably corrupt the reduction
> that was already in progress. So the pending finalisers need to be
> held off until a "safe" moment, but I haven't yet been able to find a
> way to determine when that is.
>> In any case the most likely definition of a Haskell finaliser is just
> a call into C-land anyway, so cut out the middle-man is what I say!
Well, looking at the big picture I suppose having Haskell finalizers
does imply a kind of pre-emptive concurrency, which might not otherwise
be present in the system. Just by adding Haskell finalizers, assuming
they can be implemented without concurrency, it is possible that IO code
can be arbitrarily interleaved. This is a pretty big step to take for a
single-threaded system, since it will likely impact other parts of the
system (libraries have to be thread-safe, etc.).
Hmm. Maybe we should have the pure C finalizer version of
addForeignPtrFinalizer, with the version with a Haskell finalizer
optional, available iff pre-emptive Concurrent Haskell is supported. We
have to specify the constraint that the C finalizer can't directly or
indirectly invoke any Haskell code (is this checkable at run time?).
Alternatively, we could have an explicit operation
runFinalizers :: IO ()
which causes finalizers to run in a non-pre-emptive or non-concurrent
system.
Cheers,
Simon