[MLton] adding synchronous finalizers

Stephen Weeks MLton@mlton.org
Fri, 1 Oct 2004 13:12:59 -0700


> I'm ok for now with only finalizers being handled.  Probably the Gtk
> people should speak up if it is a problem for their use.

We're not taking any functionality away, so it shouldn't hurt.
Although it would be nice to know if this is the kind of synchronous
behavior they want.  Henning Niss is on the list, but I don't know how
closely he follows -- Henning, are you reading this?

> >From my understanding of your code (and my knowledge of the MLton
> run-time system is VERY weak with regards to any of the
> multi-threaded stuff), won't it pay attention to the flag only when
> an object is added to the finalization world?  That certainly isn't
> what I would have expected: I would think that setting
> Finalizable.finalizeSynchronously to true would mean that no
> finalizers would be called (except via Finalizable.runFinalizers)
> until I set it to false.  Also, when I set it to false I would
> expect all finalizers, even ones made finalizable while it was true,
> to then run automatically.

You're right.  And I like your proposed behavior better.  This kind of
difference is one argument for making finalizeSynchnronously a
compile-time constant -- in that case our proposals are identical. In
any case, here's an attempt at a patch for your semantics.

----------------------------------------------------------------------
structure Finalizable:
   sig
      include MLTON_FINALIZABLE

      val runFinalizers: unit -> unit
      val setRunSynchronously: bool -> unit
   end =
   struct
      structure F = MLton.Finalizable

      open F
	 
      val synchronously: bool ref = ref false
 
      val finalizers: (unit -> unit) list ref = ref []
	 
      fun addFinalizer (v, f) =
	 F.addFinalizer
	 (v, fn a =>
	  if !synchronously
	     then finalizers := (fn () => f a) :: !finalizers
	  else f a)
	    
      fun runFinalizers () =
	 List.app (fn f => f ())
	 (MLton.Thread.atomically
	  (fn () => !finalizers before (finalizers := [])))
   
      fun setRunSynchronously b =
	 if b = !synchronously
	    then ()
	 else (synchronously := b
	       ; if b
		    then ()
		 else runFinalizers ())
   end
----------------------------------------------------------------------

Another possible behavior would be to report an error if the client
does setRunSynchronously false and there are pending finalizers.

Given the messiness with changing the synchronousness, maybe it
would be better to go with a compile-time constant.

> I  think  that it is important to realize that there are two distinct reasons
> (well, at least two) for including things in MLton instead of  having  people
> implement  them  themselves: one is that you can't implement it unless you go
> into the run-time system.  Another one, and I think the one that is important
> here,  is  when  it  is  important  that  all  uses of a facility use it in a
> compatible way.  I.e., if I want  to  run  some  code  with  only  synchronus
> finalization,  I  want  that  to be the case even for finalization of objects
> that I have nothing to do with creating.  That seems to  require  a  program-
> wide agreement, and so argues strongly to put it in MLton.Finalizable

Good point.