Beefy Boxes and Bandwidth Generously Provided by pair Networks
laziness, impatience, and hubris
 
PerlMonks  

Re^2: XS: Manipulating refcounts

by Animator (Hermit)
on Sep 20, 2006 at 11:19 UTC ( [id://573867]=note: print w/replies, xml ) Need Help??


in reply to Re: XS: Manipulating refcounts
in thread XS: Manipulating refcounts

I played a bit with it...

This is the only thing I can come up with:

  • sv_setref_pv() upgrades perlref to an RV and returns it,
  • you then store this SV in artist->perlobj,
  • you return the SV. Because of some XS magic (I think) a new SV is created with the contents of the perlref SV,
  • then later in your code you increase the reference counter of the original SV. (perlref - but this is not the same SV as $artist, and even if it were then it would still be wrong since you should be incrementing the reference counter of $$artist).

What you should be doing instead (I think) is store the thing it refences to in perlobj and increase the counter of that.
That is: use:

sv_setref_pv(perlref, "Artist", (void*)artist); artist->perlobj = SvRV(perlref);
instead of:
artist->perlobj = sv_setref_pv(perlref, "Artist", (void*)artist);

The attached code does that and is (IMHO) a bit easier to understand...

#!/usr/bin/perl -l 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 *perlref; Artist *artist; printf("Creating artist: %s\n", name); /* 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); sv_setref_pv(perlref, "Artist", (void*)artist); artist->perlobj = SvRV(perlref); return perlref; } /* Constructor for CD. */ SV* new_cd(SV *artist_sv) { CD *cd; Artist *artist = (Artist*) SvIV(SvRV(artist_sv)); SV *cd_ref; printf("Creating CD (artist = %s)\n", artist->name); /* allocate and initialize CD struct */ New(0, cd, 1, CD); cd->artist = artist; 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); } SV* refcnt (SV *sv) { return newSViv(SvREFCNT(sv)); } 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 ($artist_name)"; main::destroy_artist($artist); } package CD; use strict; use warnings; sub DESTROY { my $cd = shift; print STDERR "CD::DESTROY called."; main::destroy_cd($cd); } package main; use strict; use warnings; { # Create an Artist object. my $artist = new_artist("Johnny Houseburner"); print 'Refcount: $artist: ' . refcnt($artist); print 'Refcount: $$artist: ' . refcnt($$artist); # Save a copy of the Artist object in a CD object. my $cd = new_cd($artist); print 'Refcount: $artist: ' . refcnt($artist); print 'Refcount: $$artist: ' . refcnt($$artist); print 'Refcount: $cd: ' . refcnt($cd); print 'Refcount: $$cd: ' . refcnt($$cd); print 'undef $artist'; undef $artist; print "Artist name via cd: " . get_artist_name_from_cd($cd); print 'undef $cd'; undef $cd; print "End block"; } print "End code";

The output of the code:

Creating artist: Johnny Houseburner Refcount: $artist: 1 Refcount: $$artist: 1 Creating CD (artist = Johnny Houseburner) Refcount: $artist: 1 Refcount: $$artist: 2 Refcount: $cd: 1 Refcount: $$cd: 1 undef $artist Artist name via cd: Johnny Houseburner undef $cd CD::DESTROY called. Artist::DESTROY called (Johnny Houseburner) End block End code

(Small note: this code is still easy to segfault: all you need to do is change $$artist.)

Replies are listed 'Best First'.
Re^3: XS: Manipulating refcounts
by creamygoodness (Curate) on Sep 20, 2006 at 18:04 UTC

    Beautifully done, Animator. Your rewrite establishes that what I'd hoped to do is possible. It's clearer IMHO as well. :)

    My crucial error was misunderstanding the return value of sv_setref_pv. I'd thought it returned the object rather than the reference. Once artist->perlobj is assigned the correct value, everything falls into place, as you show. Now I can go implement this algo. Brilliant!

    you return the SV. Because of some XS magic (I think) a new SV is created with the contents of the perlref SV,

    Here's how things progress behind the scenes as control leaves new_artist and we enter XS_main_new_artist, the glue function generated by Inline::C/xsubpp:

    XS(XS_main_new_artist) { dXSARGS; if (items != 1) Perl_croak(aTHX_ "Usage: main::new_artist(name)"); PERL_UNUSED_VAR(cv); /* -W */ { char * name = (char *)SvPV_nolen(ST(0)); SV * RETVAL; RETVAL = new_artist(name); ST(0) = RETVAL; sv_2mortal(ST(0)); } XSRETURN(1); }
    1. The return value from new_artist is copied to RETVAL, which is copied in turn to ST(0), the top of the perl stack.
    2. ST(0) is "mortalized" by calling sv_2mortal( ST(0) ). That's essentially a delayed decrement of its refcount. It won't be freed right away as would be the case if you'd called SvREFCNT_dec -- instead, the cleanup will occur a bit later, after we've had the chance to do something with it.
    3. XSRETURN(1) is invoked, indicating that a single value is being returned via the stack, and control leaves XS land, heading back into world of stock Perl OPs.
    4. The Perl assignment operator = pops the top item off the stack and triggers a call to SvSetMagicSV, and the value of the popped SV is copied into the lexical variable $artist. Now the SV that we created back in new_artist can be freed. (Note that since the Perl interpreter can often tell when an SV is about to be freed, it will probably "steal" the contents and avoid doing unneeded memory allocation and copying).

    Now, consider that in my original program, the mortalized SV on the stack is the same SV that's housed in the Artist struct. I don't fully understand how reference counts affect things in such cases, but that was definitely not what I intended. It does not surprise me that undesirable consequences arose.

    Thanks for your help,

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

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://573867]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others chilling in the Monastery: (6)
As of 2024-03-28 11:50 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found