3

私はこれに相当する 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)

ポイントは:

  1. コンストラクターにパラメーターとして渡された基本参照は決してリークしません。
  2. XSコードで作成されたベースsv_2mortal(newRV_noinc((SV *) newHV()))もリークしません。
  3. で作成されたベースnewRV_noinc(sv_2mortal((SV *) newHV()))はリークします (そして最終的にはグローバルな破壊中に悪名高いAttempt to free unreferenced scalar: SV 0xdeadbeefメッセージを引き起こします)。

sv_2mortal(newRV_noinc(...))と異なる理由はありますnewRV_noinc(sv_2mortal(...))か?私は単にそれを間違っていますか?

4

1 に答える 1

5

sv_2mortalrefcount の遅延デクリメントです。(これは、呼び出し元が参照を取得またはコピーする機会があった後に発生します。) この投稿では、参照カウントをsv_2mortalすぐに減少させるかのように指定しますが、これを示すためにアスタリスク ("*") を使用します。


次のコードは、参照を死滅させます。

sv_2mortal(newRV_noinc((SV*)newHV()))

そう、

  1. ハッシュには REFCNT=1 があります。作成した参照はそれへの参照を保持するため、これは良いことです。
  2. 参照には REFCNT=0* があります。何も参照していないので、これは良いことです。

次のコードは、ハッシュを死滅させます。

newRV_noinc(sv_2mortal((SV*)newHV()))

そう、

  1. ハッシュには REFCNT=0* があります。あなたが作成した参照はそれへの参照を保持しているため、これは悪いことです。時期尚早の割り当て解除につながります (参照が解放されたときに「参照されていないスカラーを解放しようとしています」というメッセージが表示されるのはそのためです)。
  2. 参照には REFCNT=1 があります。何も参照していないため、これは悪いことです。これは漏れです。
于 2012-11-27T02:18:36.423 に答える