RE: Minor tweaks to ffi addendum
section 5.5:
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. I don't want the garbage collector to be recursively invoked so I don't want the garbage collector to directly invoke Haskell function. The Hugs garbage collector is not supposed to be a mutator of the heap - so putting a simple wrapper round the garbage colector won't work. And there's no mechanism outside the GC to look for cleanup functions to execute.
GHC gets round this by scheduling a (preemptive) thread to execute the cleanup code. How on earth does NHC get round this? Does anyone have a suggestion for how it might be implemented in Hugs?
Proposed change: none at present but I'm deeply sceptical of a design which takes a simple task (invoke a C cleanup function) and, for no discernable reason, generalizes it to the point that it requires a much more complex runtime system.
I've attached some mail I dug up on the subject, from before the creation of ffi@haskell.org (there's more in the archives of that list). The upshot is: - Haskell finalizers are much more in keeping with the general philosophy of the FFI: do as much in Haskell as possible. It looks a bit strange if you have to do your allocation in Haskell but deallocation in C. - If we forced finalizers to be C functions because calling Haskell from the GC is inconvenient, then you have to add a constraint that the C function invoked from a finalizer can't call any functions foreign-exported from Haskell land. That's an annoying constraint to have to add, because it means that C libraries can't be transparent w.r.t. whether they invoke Haskell code or not (actually the hs_init() problem that someone else brought up recently also has this side-effect). 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. The equivalent would be to add a check in Hugs's eval loop, I imagine.
Table 2 (section 6):
Is there a reason to be so coy about the concrete C types used for HsChar, HsFloat, HsDouble and HsBool?
Proposed change:
C symbol Haskell Symbol Constraint on concrete C type HsChar Char unsigned char
HsChar is an unsigned 32-bit int in GHC. We probably ought to say that the type is unsigned, at least - and the Haskell 98 standard requires that it can at least contain all the Unicode character values (20 bits?). Cheers, Simon
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! Regards, Malcolm
- If we forced finalizers to be C functions because calling Haskell from the GC is inconvenient, then you have to add a constraint that the C function invoked from a finalizer can't call any functions foreign-exported from Haskell land. That's an annoying constraint to have to add, because it means that C libraries can't be transparent w.r.t. whether they invoke Haskell code or not (actually the hs_init() problem that someone else brought up recently also has this side-effect).
What do finalizers do? For the most part, all they do is call other finalizers then free up the resources they directly hold. A Haskell finalizers will usually look like this: void freeFoo(Foo f) { hs_freeStablePtr((HsStablePtr)f); } So finalizers have to be able to invoke some Haskell runtime functions but don't have to be able to invoke Haskell finalization code directly. Btw the runtime functions we found it useful to export from Hugs were: void freeStablePtr(HsStablePtr); void garbageCollect(); I don't think there are any others. I'm not sure if garbageCollect is normally called by a finalizer but, if it were, I think we could arrange for that limited form of reentrancy to work by exploiting the fact that the GC invokes all finalizers at the end of the GC - so the heap is in a nice clean state when it happens. It's also possible that finalizers might 'do something' like close a window (maybe showing an amusing animation while they do so). 1) These are much rarer. I don't remember the last time I saw this being done. In fact, I always considered it something of a mistake to rely on automatic finalizers to do anything with human visible consequences (though I seem to remember that Self or Smalltalk relied on this for closing windows and found it very effective). 2) C programmers are already familiar with the idea that not all code is reentrant and that you're especially likely to hit reentrancy problems in a finalizer. So whilst it is a greater burden it is not a new burden unlike any they normally deal with. 3) If they're using GHC, they are free to specify that their imported finalizer is 'safe' (or threadsafe) and that 'safe' finalizer can happily call back info the runtime. -- Alastair Reid reid@cs.utah.edu http://www.cs.utah.edu/~reid/
participants (3)
-
Alastair Reid -
Malcolm Wallace -
Simon Marlow