2012-10-04 28 views
7

Estoy tratando de escribir un código XS que expone piezas de una biblioteca a código Perl como una interfaz de flujo que se puede escribir en. La función siguiente get_stream se supone que es un constructor que prepara y devuelve un objeto PerlIO. Pensé que solo necesitaba los métodos Write y Close, así que dejé en blanco todas las demás ranuras de función.¿Por qué este código XS que devuelve PerlIO * tiene fugas?

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 

Cuando uso la interfaz proporcionada como esto ...

{ 
    my $fh = MyLib::get_stream($lib_ctx); 
    print $fh "x" x 300; 
} 

... mylib_write la función es llamada, por lo que tengo no totalmente atornilla hasta ahora. (He verificado esto insertando instrucciones printf de depuración). Sin embargo, me gustaría que el objeto PerlIO se cierre cuando $fh sale del alcance, tal y como funcionan las cosas con filehandles regulares creados por open. Pero por el momento, la función mylib_close solo se invoca durante el apagado del intérprete.

llamando directamente close funciona bien, el establecimiento de $fh a undef no se .

ACTUALIZACIÓN: Siguiendo el consejo de Ikegami, solía Devel::Peek::Dump y sv_dump , y se informó de que el asa volvió get_stream función es una "RV" que apunta a una SV = PVGV(...). El glob (PVGV) tiene su contador de referencia establecido en 3, lo que no parece correcto.

I añadió

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

que cura el síntoma: La función close se llama cuando $fh sale del ámbito en el extremo del bloque. Pero todavía no entiendo el problema subyacente.

Esto se genera el código C para la sección 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); 

¿Cómo termina contador de referencia del GV a las 3?

+0

ug, toda nueva pregunta en la misma en el mismo puesto – ikegami

Respuesta

7

Si se llama a close en destrucción global, eso significa que su asa aún existe en la destrucción global. ¡Estás goteando!

En el código C/XS, puede usar sv_dump(sv) para volcar un escalar a stderr. En el código de Perl, puede usar Devel::Peek 's Dump para obtener la misma funcionalidad. Esto le mostrará recuentos de referencia.


En respuesta a su pregunta nueva,

tiene tres asignación, pero sólo una cancelación de asignación (tardío de sv_2mortal).

  • gv: Puntero siempre descartado. ¡Pérdida de memoria!

    Puede disminuir el error de gv en caso de error, o disminuir el refcnt incondicionalmente después de usar newRV_inc para "transferir la propiedad" a la RV cuando se abre correctamente.

  • SV de newRV: Puntero siempre descartado. ¡Pérdida de memoria!

    ¿Por qué no devolverlo en lugar de copiarlo? Simplemente márcalo como mortal para hacer que Perl reduzca su reflujo después de que la persona que llama lo obtenga.

fijo:

{ 
    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

Cuando agrego '' Pushed' y Popped'? funciones, veo que 'Popped' solo se llama durante el cierre global del intérprete. – hillu

+1

Me extrañaba que se llamara' close' en ese momento. (Pensé que habías dicho que no se llamaba en absoluto). Tu identificador de archivo está sobreviviendo hasta la destrucción global. Se está escapando – ikegami

+0

Actualizado en base a esta información nueva para mí. – ikegami

0

simplemente reproduje el tema con un ejemplo trivial:

$ 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 

Para foo/foo.xs, añadí:

PerlIO* 
get_stream(char* name); 
CODE: 
RETVAL = PerlIO_open (name, "w"); 
OUTPUT: 
RETVAL 

y la siguiente trivial Programa de prueba:

#!/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"; 

Efectivamente, el contador de referencias de la glob generada se establece en 3, y strace revela que el cierre del descriptor de archivo es la última cosa que el intérprete Perl hace.

Por lo tanto, el manejo de PerlIO* parece tener fugas por defecto. :-(

El siguiente fragmento typemap parece solucionar este problema (gracias, 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; 
     } 
    } 
Cuestiones relacionadas