Reputation: 5220
I am trying to do a XS equivalent of this:
package RefTestPP;
use strict;
use warnings;
sub new {
my ($class, $self) = (@_, {});
return bless $self, $class;
}
1;
This kind of constructor is supposed to "autovivify" it's base when called as RefTestPP->new()
, or use the given reference as a base, like RefTestPP->new({ stuff => 123 });
.
However, I am experiencing unexplainable leaks. Here's my RefTest.xs
file:
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#include "ppport.h"
MODULE = RefTest PACKAGE = RefTest
PROTOTYPES: ENABLE
void
new(sclass="RefTest", base=sv_2mortal(newRV_noinc((SV *) newHV())))
const char *sclass
SV *base
PREINIT:
HV *stash;
PPCODE:
stash = gv_stashpv(sclass, 0);
ST(0) = sv_bless(base, stash);
XSRETURN(1);
void
new_leaky(sclass="RefTest", base=newRV_noinc(sv_2mortal((SV *) newHV())))
const char *sclass
SV *base
PREINIT:
HV *stash;
PPCODE:
stash = gv_stashpv(sclass, 0);
ST(0) = sv_bless(base, stash);
XSRETURN(1);
And the RefTest.t
file which detects the leak:
use strict;
use warnings;
use Devel::Leak;
use Test::More;
BEGIN { use_ok('RefTest') };
sub test_leak (&$;$) {
my ($code, $descr, $maxleak) = (@_, 0);
my $n1 = Devel::Leak::NoteSV(my $handle);
$code->() for 1 .. 1000;
my $n2 = Devel::Leak::CheckSV($handle);
cmp_ok($n1 + $maxleak, '>=', $n2, $descr);
}
# OK
test_leak { my $ref = RefTest->new() or die }
'first sv_2mortal(); then newRV_noinc()', 2;
# also OK
test_leak { my $ref = RefTest->new_leaky({}) or die }
'first sv_2mortal(); then newRV_noinc(); pre-init base', 2;
# leaks!
test_leak { my $ref = RefTest->new_leaky() or die }
'first newRV_noinc(); then sv_2mortal()', 2;
done_testing 4;
(the remaining files required for proper compilation are the defaults generated by h2xs -A -n RefTest
)
The point is:
sv_2mortal(newRV_noinc((SV *) newHV()))
doesn't leak, also;newRV_noinc(sv_2mortal((SV *) newHV()))
will leak (and eventually cause the infamous Attempt to free unreferenced scalar: SV 0xdeadbeef
message during global destruction).Is there any reason for sv_2mortal(newRV_noinc(...))
to be different from newRV_noinc(sv_2mortal(...))
? Am I simply doing it wrong?
Upvotes: 3
Views: 265
Reputation: 385897
sv_2mortal
is a delayed refcount decrement. (It happens after the caller has a chance to get reference it or copy it.) In this post, I shall give the ref counts as if sv_2mortal
decrements immediately, but will use a star ("*") to indicate this.
The following code mortalises the reference:
sv_2mortal(newRV_noinc((SV*)newHV()))
So,
The following code mortalises the hash:
newRV_noinc(sv_2mortal((SV*)newHV()))
So,
Upvotes: 5