[MLton-devel] finalization

Stephen Weeks MLton@mlton.org
Fri, 9 May 2003 15:41:47 -0700


I agree that we don't want to tie thread scheduling to finalization.
I like Matthew's approach.   So, I propose to implement:

signature AFTER_GC =
   sig
      val afterGC: (unit -> unit) -> unit
   end

Here's the implementation, which relies on two new thread primitives:

	val finishAfterGC: thread -> unit
	val setAfterGC: thread -> unit

------------------------------------------------------------
functor AfterGC (structure Prim:
		    sig
		       structure Thread:
			  sig
			     type t

			     val finishAfterGC: t -> unit
			     val saved: unit -> t
			     val setAfterGC: t -> unit
			     val switchTo: t -> unit
			  end
		    end
		 structure MLtonExn:
		    sig
		       val topLevelHandler: exn -> 'a
		    end
		 structure Thread:
		    sig
		       type 'a t

		       val new: ('a -> unit) -> 'a t
		       val toPrimitive: unit t -> Prim.Thread.t
		    end): AFTER_GC =
   struct
      val afterGC =
	 let
	    val r: (unit -> unit) list ref  = ref []
	    fun loop () =
	       let
		  val t = Prim.Thread.saved ()
		  val _ = List.app (fn f => f ()) (!r)
		  val _ = Prim.Thread.finishAfterGC t
	       in
		  loop ()
	       end
	    val _ =
	       Prim.Thread.setAfterGC
	       (Thread.toPrimitive
		(Thread.new
		 (fn () => loop () handle e => MLtonExn.topLevelHandler e)))
	 in
	    fn f => r := f :: !r
	 end
   end
------------------------------------------------------------

To implement the primitives, add the following two fields to GC_state:

	GC_thread afterGC;
	bool amInAfterGC;

Then setAfterGC just sets the appropriate field in GC state.

void Thread_setAfterGC (Thread t) {
	gcState.afterGC = (GC_thread)t;
}

Add the following lines in GC_gc after the call to doGC to switch to
the afterGC thread if there is one.  Also, enter a critical section so
that signal handlers don't run during finalization and set a flag to
ensure that if a nested GC happens then the afterGC thread won't be
re-entered.

		if (BOGUS_THREAD != s->afterGC 
			and not s->amInAfterGC
			and not s->inSignalHandler) {
			s->canHandle++;
			s->amInAfterGC = TRUE;
			s->savedThread = s->currentThread;
			switchToThread (s, s->afterGC);
		}

To implement finishAfterGC, we leave the critical section, clear the
flag, and switch back to the saved thread.

void Thread_finishAfterGC (Thread t) {
	GC_finishAfterGC (&gcState, (GC_thread)t);
}

void GC_finishAfterGC (GC_state s, GC_thread t) {
	s->canHandle--;
	s->amInAfterGC = FALSE;
	GC_switchToThread (s, t);
}

That's it.  If you want to, you can have critical sections that
prevent finalizers by turning amInAfterGC into a counter just like
canHandle.

Here's my proposed implementation of finalizers on top of AFTER_GC and
WEAK.

signature WEAK =
   sig
      type 'a t

      val get: 'a t -> 'a option
      val new: 'a -> 'a t
   end

signature FINALIZE =
   sig
      val finalize: 'a * (unit -> unit) -> unit
   end

functor Finalize (structure AfterGC: AFTER_GC
		  structure Weak: WEAK): FINALIZE =
   struct
      val finalize =
	 let
	    val r: {clean: unit -> unit,
		    isAlive: unit -> bool} list ref = ref []
	    val _ =
	       AfterGC.afterGC (fn () =>
				r :=
				List.foldl
				(fn (z as {clean, isAlive}, ac) =>
				 if isAlive ()
				    then z :: ac
				 else (clean (); ac))
				[] (!r))
	 in
	    fn z => r := z :: !r
	 end

      val finalize =
	 fn (a: 'a, f: unit -> unit) =>
	 let
	    val w = Weak.new a
	    fun isAlive () = isSome (Weak.get w)
	 in
	    finalize {clean = f, isAlive = isAlive}
	 end
   end

Henry, I didn't completely follow what you said:

> Maybe Matthew's installAfterGC is the way to go, but then you would
> need to have some kind of hook to delay the finalization.  If `user
> code' implemented its own stuff using installAfterGC, then all is
> well, but if we add finalized objects to the `system', then code
> which needed to do this would have to not use ours and modify the
> code.

Does the implementation above have this problem?


-------------------------------------------------------
Enterprise Linux Forum Conference & Expo, June 4-6, 2003, Santa Clara
The only event dedicated to issues related to Linux enterprise solutions
www.enterpriselinuxforum.com

_______________________________________________
MLton-devel mailing list
MLton-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/mlton-devel