Perl 在XS代码中添加空引用时泄漏

Perl 在XS代码中添加空引用时泄漏,perl,memory-leaks,reference,garbage-collection,xs,Perl,Memory Leaks,Reference,Garbage Collection,Xs,我正在尝试做一个与此类似的XS: package RefTestPP; use strict; use warnings; sub new { my ($class, $self) = (@_, {}); return bless $self, $class; } 1; 这种构造函数被称为RefTestPP->new(),或者使用给定的引用作为基础,比如RefTestPP->new({stuff=>123}) 然而,我遇到了无法解释的漏洞。这是我的RefTest.xs文件:

我正在尝试做一个与此类似的XS:

package RefTestPP;
use strict;
use warnings;

sub new {
    my ($class, $self) = (@_, {});
    return bless $self, $class;
}

1;
这种构造函数被称为
RefTestPP->new()
,或者使用给定的引用作为基础,比如
RefTestPP->new({stuff=>123})

然而,我遇到了无法解释的漏洞。这是我的
RefTest.xs
文件:

#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);
以及检测泄漏的
RefTest.t
文件:

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;
(正确编译所需的其余文件是由
h2xs-A-n RefTest
生成的默认文件)

重点是:

  • 作为参数传递给构造函数的基引用永远不会泄漏
  • sv2motal(newRV_noinc((sv*)newHV())在XS代码内部创建的基也不会泄漏
    
  • 使用
    newRV_noinc(sv_2mortal((sv*)newHV())
    创建的基将泄漏(并最终导致臭名昭著的
    尝试在全局销毁期间释放未引用的标量:sv 0xdeadbeef
    消息)

  • 是否有任何原因使
    sv\u2mortal(newRV\unoinc(…)
    不同于
    newRV\unoinc(sv\u2mortal(…)
    ?我只是做错了吗?

    sv\u 2mortal
    是一个延迟的refcount递减。(这发生在调用方有机会获得引用或复制它之后。)在这篇文章中,我将给出ref计数,就像
    sv2mortal
    立即递减一样,但将使用星号(“*”)来表示这一点


    以下代码使引用无效:

    sv_2mortal(newRV_noinc((SV*)newHV()))
    
    所以

  • 哈希的REFCNT=1。这很好,因为您创建的引用保留了对它的引用
  • 该引用的REFCNT=0*。这是好的,因为没有任何引用它

  • 下面的代码将使散列无效:

    newRV_noinc(sv_2mortal((SV*)newHV()))
    
    所以

  • 散列的REFCNT=0*。这是错误的,因为您创建的引用保留了对它的引用。这将导致过早释放(这就是为什么在释放引用时会出现“尝试释放未引用的标量”)
  • 该引用的REFCNT=1。这是不好的,因为没有任何引用它。这是个漏洞

  • 非常感谢@ikegami,这件事困扰了我好几个月。顺便问一下,有没有更合适的方法来创建XS代码内部的引用?