21/09/15 18:41, David Matthews wrote:
On 21/09/2015 16:08, Phil Clayton wrote:
What I am actually observing is that finalizers are not run on exit for finalizable values that are in scope in the top-level environment. On exit, the REPL has finished, so shouldn't such values be garbage collected and therefore finalized?
I noticed that but it's very difficult to fix. The problem is that the top level environment is an array contained in the executable. It is treated as "initialised data" by the linker and loader. The garbage collector treats mutable data in the executable as roots for garbage collection which means it never has to look at the immutable data in the executable. The only way I can see of fixing the problem is to copy all the data, both mutable and immutable, from the executable into the heap.
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.
As I wrote that it occurred to me that there might be some way of processing the current finaliser list during the shut-down process so that only weak refs that were referenced by other finalisers were preserved. This would allow the detection of dependencies, which is what is really needed at that point. This would be much cheaper and could be done instead of the current call to PolyML.fullGC.
This reminded me of a similar discussion a while ago: http://lists.inf.ed.ac.uk/pipermail/polyml/2012-August/001050.html
Matthew Fluet suggested that just working through finalizers, ignoring garbage collection, could break certain invariants. I think I was only in favour of ignoring garbage collection given the limitations of setFinal, which don't apply here. My inclination is to see how we get on with iterating PolyML.fullGC on exit.
I've merged the current Finalizer branch into master on github.
I've been testing Finalizable and found a few things.
1. A finalizer that raises an exception causes the cleaning thread to terminate. Attached patch 0001 handles exceptions from finalizers and reports them.
2. 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.
3. 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.
Please feel free to mangle/ignore patches as you see fit.
Phil