17/09/15 16:12, David Matthews wrote:
On 17/09/2015 11:50, Phil Clayton wrote:
Thanks for the example - it was a useful to see how to use the Thread
structure. And to see how easy it was. That's a nice library.
Attached is a first stab at implementing the FINALIZABLE signature shown
below, which is identical to MLTON_FINALIZABLE. The code is based on
the MLton implementation but with a number of differences. As mentioned
in the other email, there seems to be an issue with finalizers not being
run when exiting via OS.Process.exit.
Phil,
I've begun by adding your code to a Finalizable branch in the github
repository. The Finalizable structure and signature are included in the
basis library when "poly" is built. It looks good and there's just a
few points.
Great!
Your definition of "touch" should be fine. I think I now understand
what's happening. At the moment at any rate the Poly/ML optimiser views
the "store" as a single entity so it won't try to reorder or remove
assignments just because they occur to locations that a more
sophisticated analysis might show to be independent. By the way, I
think perhaps the reason you had that rather bizarre behaviour with your
previous definition of "touch" was that the address of the token was
still in a register. Calling "print" before calling PolyML.fullGC will
have replaced the address with something else.
I was wondering how finalisation should this interact with saving state?
Should the state of the finalizers be restored when loadState is
called? That is what will happen with the code as it currently is but
it is possible to change that so that the state is not affected by
loadState by using "non-overwritable" references.
That's an interesting question. If there is a case for using a
finalizable value on resources whose state is entirely captured in the
mutable and immutable store, then the finalizable state should probably
be restored. Most uses I've seen are for C pointers which wouldn't be
available in a new session but this is already a limitation of vols. At
the moment I can't think of a reason to use non-overwritable refs.
I've altered the initialisation code slightly. It now uses
PolyML.onEntry to start the thread and install the atExit handler. This
ensures that these are run on every run. Have a look at
basis/BasicStreamIO.sml which also contains a "non-overwritable"
reference for the list of streams that must be closed when Poly/ML exits.
I spotted this too after building a test case as a stand-alone
executable and nothing happened...
I wonder if the finalizers should be called outside updatePendingList
i.e. after "mutex" has been released? The problem at the moment is that
if a finalizer calls "new" it will deadlock. A finalizer should
probably not be creating a new finalizer but equally we don't want
deadlock.
Good point. That was easily fixed by making the function clean return a
list of functions to run instead of a flag. See attached patch (created
with git format-patch). (I can't do pull requests at the moment because
Github no longer works properly with Firefox 16.)
Regards,
Phil
>From d0f11c79944826be3a26780634ad2227793da612 Mon Sep 17 00:00:00 2001
From: Phil Clayton <[email protected]>
Date: Thu, 17 Sep 2015 18:31:31 +0100
Subject: [PATCH] Avoid deadlock due to Finalizable.new in finalizer
Finalizers must be run after the lock on pendingList is released because
Finalizable.new needs to acquire the lock to add to pendingList.
---
basis/Finalizable.sml | 15 ++++++++-------
1 files changed, 8 insertions(+), 7 deletions(-)
diff --git a/basis/Finalizable.sml b/basis/Finalizable.sml
index 057743a..3e2a1d1 100644
--- a/basis/Finalizable.sml
+++ b/basis/Finalizable.sml
@@ -70,12 +70,12 @@ structure Finalizable :> FINALIZABLE =
fun clean ((), ps) =
foldl
(
- fn (p as {isAlive, runFinalizers}, (changed, ps)) =>
+ fn (p as {isAlive, runFinalizers}, (runNowFns, ps)) =>
if isAlive ()
- then (changed, p :: ps)
- else (runFinalizers (); (true, ps))
+ then (runNowFns, p :: ps)
+ else (runFinalizers :: runNowFns, ps)
)
- (false, [])
+ ([], [])
ps
fun swap (a, b) = (b, a)
@@ -85,7 +85,7 @@ structure Finalizable :> FINALIZABLE =
fun threadFn () = (
Thread.Mutex.lock Weak.weakLock;
while true do (
- ignore (updatePendingList clean ());
+ app (fn f => f ()) (updatePendingList clean ());
Thread.ConditionVar.wait (Weak.weakSignal, Weak.weakLock)
)
)
@@ -96,9 +96,10 @@ structure Finalizable :> FINALIZABLE =
fun loop ps =
let
val () = PolyML.fullGC () (* PolyML.fullGC is synchronous *)
- val (changed, ps') = clean ((), ps)
+ val (runNowFns, ps') = clean ((), ps)
in
- if changed
+ app (fn f => f ()) runNowFns;
+ if not (null runNowFns)
then loop ps'
else ()
end
--
1.7.7.6
_______________________________________________
polyml mailing list
[email protected]
http://lists.inf.ed.ac.uk/mailman/listinfo/polyml