On 22/09/2015 00:52, Phil Clayton wrote:
I see what you mean. I was thinking only about an interactive/batch session. For an executable, it's not clear that it makes sense to have a finalizable value at the top-level. Already something odd is happening: in the attached example toplevel_eg1.sml, the executable has a finalizer depending on whether PolyML.fullGC was called when building.
I've merged the current Finalizer branch into master on github.
I've been testing Finalizable and found a few things.
- A finalizer that raises an exception causes the cleaning thread to
terminate. Attached patch 0001 handles exceptions from finalizers and reports them.
- I have an FFI-related test suite for checking finalization that
produces an output log. Now that finalizers are called asynchronously, the log entries are out of place and the order is not deterministic. Even carefully ordering text output and always flushing does not entirely solve the problem: writing to stdOut from different threads seems to lead to some corruption. What I needed was a function to synchronously run all finalizers (like on exit) in the main thread. I have added a function doGCAndFinalize in the attached patch 0002. I'm not convinced by this patch but it resolves this issue.
- For 5.5.2, when finalizeBefore is used, it appears that not all
finalizers are run by repeatedly calling PolyML.fullGC. This occurs on exit and, with patch 0002 applied, when doGCAndFinalize is called. See attached example test1.sml.gz. It appears that this has been fixed in the current development by commit d9ca031dd99161c93ba03c42af396dafbf8c8482 so I mention that just for information. I have no immediate need for finalizeBefore, so this should not hold me up.
Phil, I've been thinking about this for a while and reluctantly I've come to the conclusion that the only option is to remove Finalizable from the basis library. The problem is with trying to give a clear definition of what it does in the presence of all these variables.
At the heart of the difficulty is the view of what the garbage collector does. The idea of the garbage collector is that it should not have an observable effect on the program and so it can be run in various forms, minor collections or major collections, at various times. It guarantees not to remove data that may be required in the future.
Adding finalisers in this way turns this on its head. It makes the GC observable and it requires, to some extent, that the collector should guarantee to remove objects that are no longer required.
I really don't think this is achievable or even desirable. Trying to achieve finalisation by using reachability makes the program unpredictable and subject to changes in quite unexpected ways. The effect of commit d9ca031dd99161c93ba03c42af396dafbf8c8482 is a specific example. This commit changed the way some run-time system calls were handled on the X86 so that registers were not preserved across RTS calls. The reason for this was to try to speed up the calls. The fact that it had an observable effect on the program is worrying to say the least.
I've added "touch" to the Weak structure so you can implement the original Finalizable structure yourself and that's probably easiest for you.
Best regards, David