#!/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 -- DESTROY # 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"; #### $ 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 $ #### my $copy = $artist; $artist = new_artist("FunThree"); #### 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); } #### /* 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); }