Greets,

I'm having some trouble manipulating refcounts at the C level to prevent an object's DESTROY method from being called. What I'd like to do is increment an object's reference count from C, do a lot of processing which may involve several trips across the Perl/C boundary, then decrement the object's reference count from C, triggering destruction.

Here's a script which illustrates the problem using our old favorite, the CD metaphor:

#!/usr/bin/perl use strict; use warnings; use Inline C => <<'END_C'; typedef struct Artist { char *name; SV *perlobj; } Artist; typedef struct CD { Artist *artist; /* ... */ } CD; /* Constructor for Artist. */ SV* new_artist(char *name) { SV *perlobj, *perlref; Artist *artist; /* allocate and initialize Artist struct */ New(0, artist, 1, Artist); artist->name = savepv(name); /* create perl object and ref; store pointer to object in struct * +/ perlref = newSV(0); artist->perlobj = sv_setref_pv(perlref, "Artist", (void*)artist); return perlref; } /* Constructor for CD. */ SV* new_cd(SV *artist_sv) { CD *cd; Artist *artist = INT2PTR( Artist*, SvIV(SvRV(artist_sv)) ); SV *cd_ref; /* allocate and initialize CD struct */ New(0, cd, 1, CD); cd->artist = artist; /* WHY DOESN'T THIS SAVE THE ARTIST FROM DESTRUCTION? */ SvREFCNT_inc(artist->perlobj); cd_ref = newSV(0); sv_setref_pv(cd_ref, "CD", (void*)cd); return cd_ref; } /* getter */ SV* get_name_from_artist(SV *artist_sv) { Artist *artist = INT2PTR( Artist*, SvIV(SvRV(artist_sv)) ); return newSVpv(artist->name, 0); } /* getter */ SV* get_artist_name_from_cd(SV *cd_sv) { CD *cd = INT2PTR( CD*, SvIV(SvRV(cd_sv)) ); return newSVpv(cd->artist->name, 0); } /* Destructor for Artist objects */ void destroy_artist(SV *artist_sv) { Artist *artist = INT2PTR( Artist*, SvIV(SvRV(artist_sv)) ); Safefree(artist->name); Safefree(artist); } /* Destructor for CD objects */ void destroy_cd(SV *cd_sv) { CD *cd = INT2PTR( CD*, SvIV(SvRV(cd_sv)) ); SvREFCNT_dec(cd->artist->perlobj); Safefree(cd); } END_C package Artist; use strict; use warnings; use Devel::Peek qw( Dump ); sub DESTROY { my $artist = shift; my $artist_name = main::get_name_from_artist($artist); print STDERR "Artist::DESTROY called.\n" . "$artist_name commits suicide -- but why?\n" . "Somebody still loves you! You have a refcount of 2:\n"; Dump($artist); main::destroy_artist($artist); } package CD; use strict; use warnings; sub DESTROY { my $cd = shift; print STDERR "CD::DESTROY called.\n"; main::destroy_cd($cd); } package main; use strict; use warnings; # Create an Artist object. my $artist = new_artist("Johnny Houseburner"); # Save a copy of the Artist object in a CD object. my $johnny_houseburner_cd = new_cd($artist); # Displace the old Artist object in Perl-space. No destruction should + occur, # because the CD object holds a reference. But that doesn't work -- D +ESTROY # gets called. $artist = new_artist("FunThree"); my $funthree_cd = new_cd($artist); # access invalid memory. my $whats_his_name = get_artist_name_from_cd($johnny_houseburner_cd); print STDERR "What was that guy's name again? $whats_his_name?\n";

And here's its output:

$ perl cd_artist.plx Artist::DESTROY called. Johnny Houseburner commits suicide -- but why? Somebody still loves you! You have a refcount of 2: SV = RV(0x182186c) at 0x1856bf8 REFCNT = 1 FLAGS = (PADBUSY,PADMY,ROK) RV = 0x1801380 SV = PVMG(0x1822080) at 0x1801380 REFCNT = 2 FLAGS = (OBJECT,IOK,pIOK) IV = 3880080 NV = 0 PV = 0 STASH = 0x180b56c "Artist" What was that guy's name again? yAGS? CD::DESTROY called. CD::DESTROY called. Bus error $

As soon as the last Perl-space reference to the Artist object goes away, Artist::DESTROY gets called, despite the fact that the reference count of the object is 2. If things were working properly, Artist::DESTROY wouldn't get called until after CD::DESTROY gets called.

We can forestall the destruction by making a copy in Perl:

my $copy = $artist; $artist = new_artist("FunThree");
... and in fact, a directly analogous maneuver of creating a new perl scalar reference from within C succeeds as well...
typedef struct CD { Artist *artist; + SV *artist_ref; /* ... */ } CD; @@ -43,7 +44,7 @@ cd->artist = artist; /* WHY DOESN'T THIS SAVE THE ARTIST FROM DESTRUCTION? */ - SvREFCNT_inc(artist->perlobj); + cd->artist_ref = newSVsv(artist_sv); cd_ref = newSV(0); sv_setref_pv(cd_ref, "CD", (void*)cd); @@ -77,7 +78,7 @@ void destroy_cd(SV *cd_sv) { CD *cd = INT2PTR( CD*, SvIV(SvRV(cd_sv)) ); - SvREFCNT_dec(cd->artist->perlobj); + SvREFCNT_dec(cd->artist_ref); Safefree(cd); }

What I don't understand, though, is how creating a new reference via newSVsv differs materially from simply incrementing the reference count of the target object. And while it's possible to add a foo_ref member for each foo member, that's messy and verbose.

/* Yecch. */ typedef struct BloatedObject { Foo *foo; SV *foo_ref; Bar *bar; SV *bar_ref; } BloatedObject; void destroy_bloated_object(BloatedObject *obj) { SvREFCNT_dec(obj->foo_ref); SvREFCNT_dec(obj->bar_ref); Safefree(obj); } /* Better. */ typedef struct LeanObject { Foo *foo; Bar *bar; } LeanObject; void destroy_lean_object(LeanObject *obj) { SvREFCNT_dec(obj->foo->perlobj); SvREFCNT_dec(obj->bar->perlobj); Safefree(obj); }

Suggestions?

--
Marvin Humphrey
Rectangular Research ― http://www.rectangular.com

In reply to XS: Manipulating refcounts by creamygoodness

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post, it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, details, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.