为什么这个返回PerlIO*的XS代码是泄漏的?

为什么这个返回PerlIO*的XS代码是泄漏的?,perl,xs,perl-io,Perl,Xs,Perl Io,我正试图编写一些XS代码,将库的各个部分公开给 Perl代码作为可写入的流接口。这个 下面的get\u stream函数应该是一个 准备并返回PerlIO对象。我想我只需要 Write和Close方法,因此我将所有其他函数槽留空 typedef struct { struct _PerlIO base; mylib_context* ctx; } PerlIOmylib; /* [...] */ PERLIO_FUNCS_DECL(PerlIO_mylib_funcs) =

我正试图编写一些XS代码,将库的各个部分公开给 Perl代码作为可写入的流接口。这个 下面的
get\u stream
函数应该是一个 准备并返回PerlIO对象。我想我只需要
Write
Close
方法,因此我将所有其他函数槽留空

typedef struct {
    struct _PerlIO base;
    mylib_context* ctx;
} PerlIOmylib;

/* [...] */

PERLIO_FUNCS_DECL(PerlIO_mylib_funcs) = {
.fsize = sizeof(PerlIO_funcs),
.name  = "mylib",
.size  = sizeof(PerlIOmylib,
.Write = mylib_write,
.Close = mylib_close,
};

/* XS below */

PerlIO*
get_stream (SV* context_obj)
CODE:
mylib_context* ctx = (mylib_context*) SvIV (SvRV (context_obj));
PerlIO* f = PerlIO_allocate (aTHX);
f = PerlIO_push (aTHX, f, PERLIO_FUNCS_CAST(&PerlIO_mylib_funcs), "a", NULL);
PerlIOSelf(f, PerlIOmylib)->ctx = ctx;
PerlIOBase(f)->flags |= PERLIO_F_OPEN;
RETVAL = f;
OUTPUT:
RETVAL
当我像这样使用提供的接口时

{
    my $fh = MyLib::get_stream($lib_ctx);
    print $fh "x" x 300;
}
。。。调用了
mylib\u write
函数,所以我还没有完全完成 到目前为止搞砸了。(我通过插入调试printf验证了这一点 但是,我希望PerlIO对象在
$fh
超出了范围,这正是正常情况下的工作方式 由
open
创建的文件句柄。但是现在,mylib\u关闭了 函数仅在解释器关闭期间调用

直接调用
close
可以正常工作,将
$fh
设置为
unde
可以 不是

更新:按照池上的建议,我使用了
Devel::Peek::Dump
sv_Dump
并发现返回的句柄
get\u stream
函数是一个“RV” 它指向一个
SV=PVGV(…)
。glob(
PVGV
)具有 参考计数器设置为3,这似乎不正确

我补充说

CLEANUP:
SvREFCNT_dec (SvRV (ST(0)));
SvREFCNT_dec (SvRV (ST(0)));
治疗症状的方法:当
$fh
在块的末尾超出范围。但我还是不太清楚 理解潜在的问题

这是为
输出
部分生成的C代码:

ST(0) = sv_newmortal();
{
    GV *gv = newGVgen("MyLib");
    if (do_open(gv, "+<&", 3, FALSE, 0, 0, RETVAL) )
        sv_setsv(ST(0), sv_bless(newRV((SV*)gv), gv_stashpv("MyLib",1)));
    else
        ST(0) = &PL_sv_undef;
}
XSRETURN(1);
ST(0)=sv_newverman();
{
GV*GV=newGVgen(“MyLib”);

如果在全局销毁时调用了(do_open(gv),+如果
close
,则表示您的句柄在全局销毁时仍然存在。您正在泄漏

在C/XS代码中,可以使用
sv\u dump(sv)
将标量转储到stderr。在Perl代码中,可以使用的
dump
获得相同的功能。这将显示引用计数


在回答你的新问题时

您有三个分配,但只有一个解除分配(SV2mortal延迟的一个)

  • gv
    :指针始终被丢弃。内存泄漏

    您可以在错误时减少
    gv
    的refcnt,也可以在打开成功时使用
    newRV_inc
    将所有权“转移”到RV后无条件地减少refcnt on

  • SV from
    newRV
    :指针始终被丢弃。内存泄漏

    为什么不直接返回它而不是复制它呢?只要将它标记为凡人,就可以使Perl在调用者得到它之后减少它的refcnt

固定的:

{
    GV *gv = newGVgen("MyLib");
    if (!do_open(gv, "+<&", 3, FALSE, 0, 0, RETVAL) ) {
        SvREFCNT_dec(gv);
        XSRETURN_UNDEF;
    }

    ST(0) = sv_2mortal(sv_bless(newRV_noinc((SV*)gv), gv_stashpv("MyLib",1))));
    XSRETURN(1);
}
{
GV*GV=newGVgen(“MyLib”);

如果(!do_open(gv),+我只是用一个简单的例子再现了这个问题:

$ h2xs -n foo
Defaulting to backwards compatibility with perl 5.14.2
If you intend this module to be compatible with earlier perl versions, please
specify a minimum perl version with the -b option.

Writing foo/ppport.h
Writing foo/lib/foo.pm
Writing foo/foo.xs
Writing foo/fallback/const-c.inc
Writing foo/fallback/const-xs.inc
Writing foo/Makefile.PL
Writing foo/README
Writing foo/t/foo.t
Writing foo/Changes
Writing foo/MANIFEST
foo/foo.xs
中,我添加了:

PerlIO*
get_stream(char* name);
CODE:
RETVAL = PerlIO_open (name, "w");
OUTPUT:
RETVAL
以及以下琐碎的测试程序:

#!/usr/bin/perl
use foo;
use Devel::Peek;
{
    my $fh = foo::get_stream ("testfile");
    Devel::Peek::Dump $fh;
    print $fh "hello\n";
}
print "bye\n";
果然,生成的glob的引用计数设置为3, 而
strace
显示关闭文件描述符是最后一次 Perl解释器所做的事情

因此,
PerlIO*
处理在默认情况下似乎是泄漏的:-(

下面的
typemap
代码片段似乎解决了这个问题(谢谢,ikegami!):

TYPEMAP
佩利奥·图皮奥
输出
图皮奥
{
GV*GV=newGVgen(“$Package”);

如果(do_open(gv,“+当我添加
push
Popped
函数时,我看到
Popped
仍然只在全局解释器关闭期间调用。我以前错过了
close
当时也被调用。(我以为你说根本没有被调用。)你的文件句柄一直保存到全局销毁。你正在泄漏。将
sv_dump(ST(0))
放入
CLEANUP
部分显示“thing”的引用计数“将
PerlIO*
转换为3。将其减少2会导致
close
在正确的时间被调用,但我仍然感到困惑。为新问题添加了答案。所讨论的C代码是由
ExtUtils::ParseXS
生成的,可能基于返回类型
(PerlIO*)
。那么…发电机有问题吗?ug,同一篇文章中的全新问题?
TYPEMAP
PerlIO *    T_PIO
OUTPUT
T_PIO
    {
        GV *gv = newGVgen("$Package");
        if (do_open(gv, "+<&", 3, FALSE, 0, 0, $var) ) {
            $arg = sv_2mortal(sv_bless(newRV_noinc((SV*)gv), gv_stashpv("$Package",1)));
        } else {
            SvREFCNT_dec(gv);
            $arg = &PL_sv_undef;
        }
    }