2012-10-04 41 views
7

我想寫暴露片庫,以 Perl代碼爲可寫入流接口一些XS碼。下面 get_stream功能應該是 準備並返回一個PerlIO的對象的構造。我想我只需要 WriteClose方法,所以我留下了所有其他功能插槽的空白。爲什麼這個XS代碼返回一個PerlIO * leaky?

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函數被調用,所以我還沒有完全 搞砸了這麼遠。 (我通過插入debug printf 語句驗證了這一點。)但是,當 $fh超出範圍時,我想要關閉PerlIO對象,只需使用由open創建的常規 文件句柄即可。但目前,只有在解釋器關閉期間調用該函數。

直接調用close優良工程,設置$fhundef不 沒有。

UPDATE:繼池上的建議下,我用Devel::Peek::Dumpsv_dump ,發現手柄返回get_stream功能是「RV」 指向一個SV = PVGV(...)。 glob(PVGV)的 引用計數器設置爲3,這看起來不正確。

我加入

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

其固化症狀:當$fh 在所述塊的端超出範圍的close函數被調用。但我還是不太明白底層的問題。

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結束了:

這是爲OUTPUT部分生成C代碼?

+0

微克,在同一職位在同一全新的問題 – ikegami

回答

7

如果close被稱爲在全球毀滅,這意味着你的手柄仍然存在於全球毀滅。你在泄漏!

在C/XS碼,您可以使用sv_dump(sv)轉儲標量標準錯誤。在Perl代碼中,您可以使用Devel::PeekDump獲得相同的功能。這將顯示您參考計數。


在回答你的新問題,

你有三種配置,但只有一個釋放(延遲一個從sv_2mortal)。

  • gv:指針總是被丟棄。內存泄漏!

    您可以在發生錯誤時減少gv的refcnt,或者在打開成功後使用newRV_inc「將所有權」轉移到RV後無條件地減少refcnt。

  • 來自newRV的SV:指針總是被丟棄。內存泄漏!

    爲什麼不直接返回而不是複製它?只要將它標記爲致命的,即在調用者獲取它之後,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

當我添加'Pushed'和'Popped'?函數,我發現'Popped'仍然只在全局解釋器關閉期間被調用 – hillu

+1

我以前錯過了'close'也被調用過(我以爲你說它根本沒有被調用)。你的文件句柄仍然存在 – ikegami

+0

根據這個新的信息更新 – ikegami

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

果然,產生水珠的引用計數設置爲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; 
     } 
    } 
相關問題