16/09/15 12:40, David Matthews wrote:
On 15/09/2015 22:18, Phil Clayton wrote:
I think weak references could do the job. Better still, I may be able to adapt (shamelessly copy) MLtonFinalizable: https://github.com/MLton/mlton/blob/master/basis-library/mlton/finalizable.s...
https://github.com/MLton/mlton/blob/master/basis-library/mlton/finalizable.s...
This would have the added bonus of a common interface for finalizable values between the compilers.
The main question is when to check the weak references. Is there some way to register a function to be called immediately after a GC? I'll investigate using a separate thread and the mutex which may be better anyway.
There's no way to register a function. Because of the way the thread system works I think the only way to do the finalisation is through a separate thread.
Understood. I think a separate thread is better anyway.
Finalizers should also be called when the ML session exits. It appears that functions registered with OS.Process.atExit are always run before Poly/ML exits (whether or not there is an explicit call to OS.Process.exit). Can you confirm that?
The intention is that that should be the case. If OS.Process.terminate is called the functions aren't run. I have just done a test and it appears that exiting by calling Thread.Thread.exit() doesn't run the atExit functions either.
I think the desirable behaviour is for finalizers to be run whenever Poly/ML is ending its own process. For example, if SIGTERM causes Poly/ML to exit, finalizers should be run because they are typically performing clean-up operations. Is there a way to do that?
Also, even using OS.Process.atExit and exiting via OS.Process.exit, I am finding that remaining finalizers aren't being run. After a call to fullGC in the at-exit function, the weakly referenced values haven't been garbage-collected, so their finalizers aren't run. Has the lifespan of those values has not ended at that that point? (See my other email for the example code.)
I was wondering how to implement the 'touch' function of MLTON_FINALIZABLE that forces a weak reference to stay alive. The expression ignore (PolyML.pointerEq (x, x) orelse raise Fail "touch"; print ""); seems to prevent Poly/ML optimizing the dependence on x away and works for any type x. Bizarrely, I found that without print "", the weak reference stayed alive. Can you think of something simpler?
I looked at the MLton documentation and couldn't understand what "touch" was trying to achieve. Could you explain it?
"touch t" prevents finalization of t until the call has been evaluated. "touch t" an operation that uses t to do nothing, so t cannot be garbage-collected until the operation has finished.
My idea with weak variables in Poly was that the "token" and the item to be finalised would be linked in such a way that they would have the same lifetime e.g. the "token" would be paired with the item.
With these finalizable values, no "item" is required, there is only a "token". The token can be a reference to anything so we make it a reference to the value that finalizers will be run on. All access to the finalizable value is by dereferencing the token, so the token is referenced as long as the finalizable value is used. This is ensured by having an abstract type requiring Finalizable.withValue to be used to access the value.
Is the issue that a global optimising compiler such as MLton could work out that the token was not referenced even though the item was and remove it from the pair at the last reference?
I don't think that is the issue here (as we don't have an item).
Poly/ML only does that in very limited circumstances. Is the idea of "touch" that this counts as a reference to the token and that you add a call to it after each reference to the item so that the lifetime of the token is no less than the lifetime of the item?
Yes, but as there is no item, the purpose of touch is to delay finalization until some other event has occurred. One example use of touch is in the implementation of Finalizable.finalizeBefore. The 'other event' is finalization of another finalizable value.
Since the token is a reference either assigning or dereferencing it should work. Even if the result is always () that should still count as a reference.
I find that ignore (!value) doesn't keep the value alive whereas value := !value does. That was a useful suggestion - much more preferable to my earlier idea. The assignment is doing work whose effect isn't required but is that an overhead worth worrying about?
Phil