2012-10-04 7 views
7

私は、書き込み可能なストリームインターフェイスとして、 Perlコードにライブラリを公開するXSコードを書くことを試みています。以下の関数 get_streamは、 がPerlIOオブジェクトを準備して返すコンストラクタであると考えられます。私は WriteCloseの方法しか必要ないと思ったので、他のすべての機能スロットは空白にしました。PerlIO *をリークするこのXSコードはなぜですか?

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関数が呼び出されますので、私は完全に がこれまでめちゃくちゃにしていません。 (私はこれを確認してデバッグprintf ステートメントを挿入して検証しました)しかし、 $fhが範囲外になるとPerlIOオブジェクトが閉じられるようにしたいのですが、 ファイルハンドルをopenで作成したものと同じです。しかし、現時点では、mylib_close 関数はインタープリタのシャットダウン時にのみ呼び出されます。

closeを直接呼び出すと、$fh~undefは に設定されます。

UPDATE:は池上のアドバイスの後、私はDevel::Peek::Dumpsv_dump を使用し、ハンドルがget_stream機能がSV = PVGV(...)を指して「RV」 で返されたことが分かりました。グロブ(PVGV)の リファレンスカウンタは3に設定されていますが、これは正しくないようです。 $fh は、ブロックの最後でスコープ外になったときclose関数が呼び出されます。

私は症状を治す

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

を追加しました。しかし、私はまだ基​​本的な問題を理解しているとはまだありません。

これは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で終わるのでしょうか?

+0

UG、同じポストでは同じで、まったく新しい質問 – ikegami

答えて

7

closeがグローバル破壊で呼び出された場合、ハンドルがグローバル破壊時に存在することを意味します。あなたは漏れている!

C/XSコードでは、sv_dump(sv)を使用して、スカラーをstderrにダンプできます。 Perlコードでは、Devel::PeekDumpを使用して同じ機能を利用できます。これにより、参照カウントが表示されます。新しい質問への答えで


次の3つの割り当て、一つだけ解放(sv_2mortalから遅れたもの)を持っています。

  • gv:ポインタは常に破棄されます。メモリーリーク!

    gvのrefcntを間違えて減らすか、オープンが成功したときにnewRV_incを使用してRVに所有権を移譲した後に、無条件にrefcntを減らすことができます。

  • SV 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); 
} 
+0

:?:-(

typemapスニペットこの(!おかげで、池上)を修正するようです関数の中で、 '' Popped''はまだグローバルインタープリタのシャットダウン中に呼び出されるだけです。 – hillu

+1

私はこれまで 'close'も呼び出されていたことを忘れていました(私はあなたが呼んでいないと思っていました)。 – ikegami

+0

この新着情報に基づいて更新されました – ikegami

0

foo/foo.xs

$ 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 

、私が追加:

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"; 

案の定、生成されたグロブの参照カウントは 、3に設定し、straceは、ファイルディスクリプタをクローズすると、Perlインタプリタがないことを最後 のものであることが明らかになったています。

だから、PerlIO*の処理は、デフォルトでは漏れているようです。私は `Pushed`と` Popped`を追加すると

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; 
     } 
    } 
関連する問題