Garbage Collection Strangeness

phillup on 2004-11-12T17:48:36

Here is a minimal script that demonstrates (on my system) the strange behavior I am experiencing.

If I uncomment the undef statement, then everything seems to work. But, if I let Perl take out the garbage it looks like my CGI::Session object gets destroyed before my own session object is destroyed. Even tho I still have a reference to the object. (Actually, my reference goes away!)

I can work around the problem by making sure I trigger the object destruction myself. But, I'd really like to know if the problem is in my mental picture of how things are supposed to work.

UPDATE: It appears to be my mental picture. ;-)

Apparently global garbage destruction is somewhat random in order. So, make sure you take out your own garbage if the order is important!

Any comments greatly appreciated!

-------

#! /usr/bin/perl

use strict; use warnings;

my $session = new My::Session;

sub skipme{ my $test = $session->{test}; # no I don't normally access the object data directly # I just need to use the object in the sub to trigger # the problem and I want to reduce the number of methods # to the bare minimum to demonstrate the problem } # sub skipme

print "\nDone\n"; #undef $session;

{#-----------------------------------------------------------------

package My::Session; use strict; use warnings; my $session_dir = '/tmp/cgisession/'; use CGI::Session::File; sub new{ # get our class of object my $CLASS = shift; my $SELF = {}; # get a new storage session $SELF->{session} = new CGI::Session("driver:File;serializer:FreezeThaw", undef, {Directory => $session_dir}) || die 'Could not get new session store!'; # and bless ourself bless $SELF, $CLASS; return $SELF; } # sub new sub DESTROY { my $SELF = shift; print "Hey!! Someone stole my session!\n" unless $SELF->{session}; } # sub DESTROY

}#-----------------------------------------------------------------


Closures.

Dom2 on 2004-11-12T17:56:05

You've got a closure there referencing $session inside skipme(). If you pass the object in instead, things will probably work as you expect.

-Dom

Re:Closures.

phillup on 2004-11-12T18:16:41

Does this mean that Perl's garbage collection doesn't destroy objects based on a reference count?

Or does the closure some how mess up the reference count?

It seems to me that the $session instance has a reference to a CGI::Session object, so the CGI::Session object should not be destroyed until after the $session variable is.

Or, do I have that wrong?

It dies during global destruction...

Elian on 2004-11-12T19:35:35

And global destruction's unordered, which is likely where your problem lies. When your program ends, perl just sweeps through all the oustanding live objects with destructors in pretty much a first-to-last order. (First to last in memory at least. This is not necessarily chronological order, nor will it necessarily be the same order from run to run of your program, depending on what its doing)

The only safe way to handle things here is make sure that your objects die before global destruction.

Re:It dies during global destruction...

jmm on 2004-11-12T20:05:26

So, in your example code, if you wrapped a block around everything from the "my $session = ..." to the "#undef ...", then the session would be closed properly when the block was exitted, but since you just "fall off the end" the remaining stuff gets discarded in arbitrary order. You would also have te same problem, I think, if you terminated with an explicit exit statement - so, as well as wrapping your mainline inside a block you could put a label (say EXIT:) on the block and change any exit statements anywhere in you program to instead say "goto EXIT" and don't use an EXIT label anywhere else.

Re:It dies during global destruction...

phillup on 2004-11-12T20:49:52

Thanks for the response. What you describe is exactly what I have concluded is happening, I just didn't think it was supposed to happen that way.

And global destruction's unordered, which is likely where your problem lies.

Do you have a reference for this? I've looked and can't find a thing about it.

I did find this in Programming Perl, 3rd Edition (page 331)
When an interpreter shuts down, all its objects are destroyed, which is important for multithreaded or embedded Perl applications. Objects are always destroyed in a separate pass before ordinary references. This is to prevent DESTROY methods from using references that have themselves been destroyed.
The last sentence led me to believe that something was being used to make sure objects don't loose the objects they are referring to during global destruction. (I assumed it was reference counting)

The only safe way to handle things here is make sure that your objects die before global destruction.

Yes, I just thought that a bit weird in the "Do The Right Thing" sense. That, and I'd not heard that the destruction was unordered before.

Re:It dies during global destruction...

phillup on 2004-11-12T21:02:34

I found someone else mentioning the fact that global destruction is unordered.

Re:It dies during global destruction...

jmm on 2004-11-13T01:25:15

As I recall (from a long time ago), it is impossible to ensure a good ordering using just ref counts because it is easy to have reference loops (e.g. two objects each with a reference to the other or an object that has a reference to itself, or a list of things that links back to the "beginning" of the list). So, since most programs don't care about the order anyhow and the ones that do can arrange things for themselves, there was no point in spending any time to do a half-hearted ordering.