[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