Subject: | Singleton objects are not always destroyed properly |
The attached program is a greatly simplified version of a problem that
I've been having in which Class::Singleton objects are not always
destroyed as I would expect.
The program creates the instance of the singleton class called One,
giving it an attribute called attr which is an instance of a simple
class called Attr, and then exits, at which point the singleton object
will be destroyed.
The problem is that although the DESTROY method of One does indeed get
called at the expected time (just after the END subroutine of One has
been called), the object that it is destroying seems to have been
partially destroyed already: the attr attribute is now the undefined
value, rather than the Attr object. The output from the program shows this:
RUNNING: attr => Attr=HASH(0x1839a68)
OK
EXITING
ENDING: attr => Attr=HASH(0x1839a68)
DESTROYING: attr => <undef>
showing that the Attr was still there when One's END subroutine was
entered, but is gone by the time One's DESTROY method is entered.
In my real software this is a problem because One's DESTROY method
actually needs to do something with Attr before setting it to the
undefined value itself.
The strange thing is that it seems rather unpredictable whether Attr
will be there or not. For example, if I change the line:
$a = One->instance(attr => Attr->new());
to:
$a = One->instance();
$a->{attr} = Attr->new();
then the output is now:
RUNNING: attr => Attr=HASH(0x1839af8)
OK
EXITING
ENDING: attr => Attr=HASH(0x1839af8)
DESTROYING: attr => Attr=HASH(0x1839af8)
showing that the Attr is still there this time! I don't understand how
that change can have made any difference.
And if I now additionally override _new_instance() in One with this
simple method which just delegates to the base class method:
sub _new_instance {
shift->SUPER::_new_instance(@_);
}
then the output is now:
RUNNING: attr => Attr=HASH(0x18464f8)
OK
EXITING
ENDING: attr => Attr=HASH(0x18464f8)
DESTROYING: attr => <undef>
once again! Again, I don't understand how that change can have made that
difference.
Is this actually an issue with perl, rather than Class::Singleton?
Perhaps the order in which it cleans up different packages and the
variables in them is dependent to some extent on what's been loaded
where in memory, or maybe it's walking through a hash, whose key/value
pairs are returned in seemingly random orders which are affected by the
insertion of new subroutines into symbol tables etc?...
Either way, I find the result is that Class::Singleton objects are not
always ideal to work with, and if anything can be done to make the
destruction of these objects behave in a more predictable manner then
that would be a great help.
This problem may be related to the trouble reported on CPAN RT #23568. I
haven't yet checked whether using the patch from that bug report would
solve the problem, but it does seem like Class::Singleton taking
explicit steps to clean up objects derived from it, rather than leaving
everything to the perils of later "automatic" clean up, might be the answer.
Subject: | test.pl |
use strict;
use warnings;
package Attr;
sub new { bless {}, shift }
package One;
use parent qw(Class::Singleton);
sub DESTROY {
print "DESTROYING: attr => ", (shift->{attr} // '<undef>'), "\n";
}
sub END {
if (my $a = One->has_instance()) {
print "ENDING: attr => ", ($a->{attr} // '<undef>'), "\n";
}
else {
print "ENDING: No instance\n";
}
}
package main;
my $a;
{
if (eval {
local $SIG{__DIE__};
$a = One->instance(attr => Attr->new());
print "RUNNING: attr => ", ($a->{attr} // '<undef>'), "\n";
1;
}) {
print "OK\n";
}
else {
print "ERROR: $@";
}
}
print "EXITING\n";