http://www.perlmonks.org?node_id=167119

khkramer has asked for the wisdom of the Perl Monks concerning the following question:

I've been poking around for the last couple of days trying to figure out some of the subtleties of global destruction. Digging around Devel::Peek-land is lots of fun, and more than a little educational. Today I found a bug in my code that results in an object's reference count being off by one. I traced the problem back to my overload'ed pre-/post-increment and copy constructor operators.

The problem is that perl increments the reference count when it invokes the copy constructor, but I'm actually trying to return the same reference: I don't want to copy, mutate or re-bless this particular class of objects. I've written some toy code to illustrate my approach and show the problem, and am hoping that someone can suggest another way to tackle this...

My class in question presents a pretty straightforward iterator interface. Each object encapsulates access to a list of records: I overload '++' to move to the next record, and 'bool' to test whether the traversal is complete. As the overload perldocs note, though, using '++' in this way implies a need to overload '=' (the copy constructor), too. Here's a stripped-down illustration:

package Toy_Iterator; use overload '""' => sub { $_[0] }, bool => sub { ! $_[0]->done_p() }, '++' => \&inc, '=' => sub { $_[0] }, ; sub new { my ( $class, $arg ) = @_; my $self = {}; bless $self, $class; $self->set ( $arg ); return $self; } sub set { $_[0]->{_i} = $_[1] } sub get { return $_[0]->{_i} } sub inc { ++$_[0]->{_i} } sub done_p { $_[0]->{_i} > 5 } sub DESTROY { print "Destroying $_[0]\n" } package main; use Devel::Peek; my $a = Toy_Iterator->new ( 2 ); while ( $a++ ) { print "$a -> " . $a->get() . "\n"; } # print "\n"; Dump $a; print "\n"; print "undef'ing \$a...\n"; undef $a; print "done undef'ing\n";

Running this script produces the following output:

Toy_Iterator=HASH(0x80fbc2c) -> 3 Toy_Iterator=HASH(0x80fbc2c) -> 4 Toy_Iterator=HASH(0x80fbc2c) -> 5 undef'ing $a... done undef'ing Destroying Toy_Iterator=HASH(0x80fbc2c)

The problem is indicated by the fact that $a's destructor is not getting called when it should, at the moment of undef'ing, but only later (during global destruction). Uncommenting out the Dump line will show that the REFCNT for the Toy_Iterator object that $a points to is 2, rather than 1 as it should be.

Changing the post-increment in the while statement to a pre-increment eliminates the problem, because the pre-increment overload here doesn't need to call the copy constructor:

Toy_Iterator=HASH(0x80fbc2c) -> 3 Toy_Iterator=HASH(0x80fbc2c) -> 4 Toy_Iterator=HASH(0x80fbc2c) -> 5 undef'ing $a... Destroying Toy_Iterator=HASH(0x80fbc2c) done undef'ing

I've been round and round the overload docs, and tried a bunch of different versions of my '++' and '=' subs. I don't want to clone these objects -- they're pretty heavy-weight and often contain open database handles. Anyway, cloning doesn't seem like the elegant thing to do for this usage case. I thought about writing the copy constructor as a bit of Inline::C that creates the reference using newRV_noinc (or something similar). But that seems like a big kludge.

Any suggestions?