7

私は、書き込み可能なストリーム インターフェイスとして、ライブラリの一部を Perl コードに公開する XS コードを書き込もうとしています。以下の get_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_write関数が呼び出されるので、これまでのところ完全に失敗していません。$fh(debug printf ステートメントを挿入することでこれを確認しました。) しかし、PerlIO オブジェクトがスコープ外になったときに閉じられるようにしたいと思います open。しかし、現時点では、このmylib_close 関数はインタープリターのシャットダウン中にのみ呼び出されます。

直接呼び出すとclose正常に機能しますが、に設定$fhするとundef機能しません。

更新:池上のアドバイスに従って、 と を使用Devel::Peek::Dumpsv_dump たところ、返されたハンドルget_stream関数が を指す「RV」であることがわかりましたSV = PVGV(...)。グロブ ( PVGV) の参照カウンターが 3 に設定されていますが、これは正しくないようです。

追加した

CLEANUP:
SvREFCNT_dec (SvRV (ST(0)));
SvREFCNT_dec (SvRV (ST(0)));

これにより症状が治り ます。ブロックの最後で範囲外になるcloseと、関数が呼び出されます。$fhしかし、私はまだ根本的な問題をよく理解していません。

OUTPUTこれは、セクション用に生成された 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);

GV の参照カウントはどのようにして 3 になるのですか?

4

2 に答える 2

0

簡単な例で問題を再現しました。

$ 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スニペットはこれを修正しているようです(ありがとう、池上!):

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;
        }
    }
于 2012-10-04T21:45:26.387 に答える