Summary

Finalizers for top level bindings don't get executed upon program exit

Metadata

Description

The following program does never print "finalized x", neither when interpreted nor compiled:

 (define x (list 1))
 (set-finalizer! x (lambda (x) (print "finalized x")))

Appending (set! x #f), it does.

Changes and comments

[2010-12-09 22:43:08 UTC] syn wrote:

Apparently this is not only true for top level bindings. This program also doesn't print anything when interpreted (but in contrast, it does when compiled!):

 (let ((x (list 1)))
   (set-finalizer! x (lambda (x) (print "finalized x"))))

[2010-12-10 09:25:58 UTC] felix wrote:

Finalizers are not called on program exit. Also, in the interpreter, results (including the result of `set-finalizer!`, which is the finalized object) are stored in the REPL history and so never freed.

[2010-12-10 18:54:00 UTC] syn wrote:

Replying to felix: > Finalizers are not called on program exit.

Is there a good reason for this or is it just not implemented? I wonder since ##sys#cleanup-before-exit in library.scm calls ##sys#force-finalizers and is registered as an exit-handler. Can you clarify this?

> Also, in the interpreter, results (including the result of `set-finalizer!`, which is the finalized object) are stored in the REPL history and so never freed.

Yep, when I said "interpreter" I meant a script executed with csi -s, not the REPL.

[2010-12-11 13:27:22 UTC] felix wrote:

Replying to syn: > Replying to felix: > > Finalizers are not called on program exit. > > Is there a good reason for this or is it just not implemented? I wonder since ##sys#cleanup-before-exit in library.scm calls ##sys#force-finalizers and is registered as an exit-handler. Can you clarify this?

Forcing finalizers will only run those that have become garbage. The fact that a process terminates does not mean all toplevel variables become garbage, which many language implementations handle that way.

I have documented this. Is this acceptable for you?

[2010-12-11 15:12:30 UTC] syn wrote:

Okay, that makes sense then, thanks! I assumed indeed that all data is considered garbage at program exit. I think this implies that programs relying on finalizers to clean up (possibly remote) state should additionally define an exit handler to be on the safe side then. Just out of curiosity: Would changing the behavior to consider all remaining top level bindings at program exit garbage and run their finalizers mean a big change and would it potentially break a lot of code? Otherwise I'd suggest changing to this behavior and maybe add a command line switch to disable it.

[2010-12-11 16:35:58 UTC] syn wrote:

I just compiled the current experimental and the let example I mentioned in the first comment here still does not work in the interpreter even when adding a (gc #t) at the end. Compiled it still works as before. Since x is not bound at the top level here, souldn't it indeed be finalized in both cases?

[2010-12-16 17:48:18 UTC] felix wrote:

Replying to syn: > Okay, that makes sense then, thanks! I assumed indeed that all data is considered garbage at program exit. I think this implies that programs relying on finalizers to clean up (possibly remote) state should additionally define an exit handler to be on the safe side then.

Yes, that would be possible.

> Just out of curiosity: Would changing the behavior to consider all remaining top level bindings at program exit garbage and run their finalizers mean a big change and would it potentially break a lot of code?

It would be a big change since all sorts of crap would start running once the program terminates. Finalizers are icky. Finalizers give you the non-determinism of threads but without the synchronization and should be avoided unless absolutely necessary (strictly speaking, they should run in a separate threads).

> Otherwise I'd suggest changing to this behavior and maybe add a command line switch to disable it.

I probably sound like an asshole, but that is also a bad idea. :-) Making behaviour like this depending on command-line- or configuration options just make maintenance unnecessary hard. I suggest you give an example that explains the motivation for what you seem to request.

[2010-12-16 17:52:12 UTC] felix removed milestone 4.7.0

[2010-12-16 17:52:12 UTC] felix wrote:

Replying to syn: > I just compiled the current experimental and the let example I mentioned in the first comment here still does not work in the interpreter even when adding a (gc #t) at the end. Compiled it still works as before. Since x is not bound at the top level here, souldn't it indeed be finalized in both cases?

The interpreter uses a simple linked-list environment-representation, and I assume that the finalizer-closure reaches back into the outer environment containing the datum. Something like this should work:

 (define (finalized x) ...)
 (begin (set-finalizer! x finalized) #f)

[2010-12-16 17:52:12 UTC] felix changed priority from major to minor

[2010-12-16 17:52:12 UTC] felix removed owner

[2010-12-16 17:52:12 UTC] felix changed component from unknown to core libraries

[2010-12-18 13:31:07 UTC] felix changed status from new to closed

[2010-12-18 13:31:07 UTC] felix set resolution to fixed