Почему этот код XS, который возвращает PerlIO*, имеет утечку?

Я пытаюсь написать некоторый код XS, который предоставляет фрагменты библиотеки для кода Perl в качестве потокового интерфейса, в который можно записать.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 Функция вызывается, так что я еще не совсем облажался. (Я проверил это, вставив отладочные операторы printf.) Однако я бы хотел, чтобы объект PerlIO был закрыт, когда$fh выходит за рамки видимости, просто то, как все работает с обычными файловыми дескрипторами, созданными open, Но на данный момент mylib_closeфункция вызывается только при выключении интерпретатора.

Прямой звонок close работает отлично, настройка $fh в undef не.

ОБНОВЛЕНИЕ: следуя совету икегами, я использовал Devel::Peek::Dump а также sv_dumpи обнаружил, что ручка вернулась get_stream функция представляет собой "RV", который указывает на SV = PVGV(...), Шар (PVGV) имеет свой счетчик ссылок, установленный на 3, что кажется неправильным.

я добавил

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

который излечивает симптом: close функция вызывается когда $fhвыходит из области видимости в конце блока. Но я все еще не совсем понимаю основную проблему.

Это код C, сгенерированный для OUTPUT раздел:

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?

2 ответа

Решение

Если close называется глобальным уничтожением, это означает, что ваша ручка все еще существует при глобальном уничтожении. Вы протекаете!

В коде C/XS вы можете использовать sv_dump(sv) сбросить скаляр в stderr. В коде Perl вы можете использовать Devel:: Peek's Dump чтобы получить ту же функциональность. Это покажет вам количество ссылок.


В ответ на ваш новый вопрос,

У вас есть три распределения, но только одно освобождение (отложенное из sv_2mortal).

  • gvУказатель всегда сбрасывается. Утечка памяти!

    Вы можете уменьшить значение refcnt gv по ошибке, или вы уменьшаете refcnt безоговорочно после использования newRV_inc "передать право собственности" на RV, когда открытие успешно.

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

Я только воспроизвел проблему с тривиальным примером:

$ 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 фрагмент кода, кажется, исправляет это (спасибо, ikegami!):

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;
        }
    }
Другие вопросы по тегам