Proposed change to ForeignPtr

Simon Marlow simonmar at
Wed Sep 11 05:24:58 EDT 2002

I'm afraid George's questions have also rekindled my curiosity about
whether implementing Haskell finalizers is really as hard as it sounds.
Much has been written, but I still don't think we've got to the nub of
the issue.

On the face of it, if you can implement 'foreign import ccall safe',
then you have a re-entrant runtime system.  The times at which the
program can make one of these foreign calls are limited, i.e. in the IO
monad only - but I believe there's nothing particularly special about IO
computations in the evaluation models used by nhc98 and Hugs (correct me
if I'm wrong).

Alastair writes:

> The way GHC implements preemption is an optimized form of: set a bit
> when preemption is needed; and make sure that generated code will test
> that bit whenever it is in a position to perform a context switch.
> What you're asking Hugs and NHC to do is: add a function to a list
> whenever you have a finalizer to run; make sure the interpreter will
> test that bit whenever it is in a position to perform a context
> switch.  
> It's basically the same.  We don't have to mess around with signals to
> provide a regular timer interrupt but that's the easy bit of the code.

Ok so far.

> We can probably avoid messing around with multiple C stacks.  That's a
> significant saving but, it's the complexity of that is fairly
> self-contained - we could probably steal some code from some other
> language implementation.
> The cost is going over all data structures in the system making sure
> that operations on them are suitably atomic.  One of the issues I
> remember from old versions of GHC was that some of the primops would
> do some work, allocate some memory, then finish the job.  The classic
> error to make in that code was for the second half of the code to
> assume almost anything about what happened in the first half of the
> code: how much space is on the stack, does a table have free space in
> it, is this pointer into the middle of an object ok?

You certainly can't keep local variables live across a heap check,
everything has to be saved on the stack.  Hugs is different because it
has a conservative GC, so doesn't need to save everything on the stack
for a GC.  But how does it implement a safe foreign call?  Presumably it
must save away state on the stack in a way that the computation can be
resumed safely, and that's all you need in order to be able to run a
Haskell finalizer.

> The problem is the scope: every single data structure and every bit of
> code that accesses it has to be vetted and we have to keep it in mind
> as we maintain the code.  It's a high price to pay and I don't think
> it's necessary (because all you really need is for the runtime systems
> to talk to each other in very limited ways).

I don't quite understand this: could you give a concrete example of some
extra invariant that has to be maintained?  In GHC, context switches
happen at very precise points (i.e. heap checks) so I don't think we
have these kind of problems; certainly I don't remember vetting every
single data structure.  Can't Hugs use a similar approach?


More information about the FFI mailing list