The Revenge of Finalizers

Alastair Reid alastair at
Mon Oct 14 17:38:55 EDT 2002

>> blockFinalizers :: IO a -> IO a

> Indeed, I very nearly implemented such a thing as part of the patch
> I sent out.  However, it didn't look trivial enough to implement so
> I backed off.  The "blocked" state needs to be saved & restored at
> various points: when starting a finalizer, when invoking a
> foreign-exported function, and when invoking an exception handler.

I think it's fairly easy.  A sketch is something like:
  // primitive called from Haskell
  void blockFinalizer(Cell m, State world) {
    blocked += 1;
    blocked -= 1;
  // function called from garbage collector when a dead foreign ptr is found
  void scheduleFinalizer(Finalizer f) {
    pending = cons(f,pending);

  // function called when GC and blockFinalizer finishes
  void runFinalizers(void) {
    if (blocked == 0) {
      while (nonEmpty(pending)) {
        Finalizer f = head(pending);
        pending = tail(pending);

There's a few details to take care of (e.g., avoiding calling cons in
scheduleFinalizer) but they're all manageable.

I think Malcolm and I can probably make this work for NHC and Hugs
without too much work.  (Malcolm: feel free to disagree.)

I think there are some difficult, unexplored issues about the
usability of this primitive.  e.g., since GHC already has MVars, when
do you use blockFinalizer, when do you use MVars and when do you use
both?  How do you write code that works on GHC, Hugs and NHC?  For
this reason, I'm not sure this is a good design and my preferences

Best: C finalizers subject to same restrictions as unsafe foreign imports.

Moderate: Haskell finalizers plus blockFinalizers.
         (May be awkward to use in portable manner?)

Poor: Haskell finalizers that may access Haskell state
      (Needlessly complex)

Worst: Haskell finalizers that may not access Haskell state
       (Introduces many problems without giving any benefits)


More information about the FFI mailing list