2010-11-29 11 views
8

Suelo utilizar fork en programas que también tienen END { ... } bloques:¿Cómo desactivo los bloques END en procesos secundarios?

... 
END { &some_cleanup_code } 
... 
my $pid = fork(); 
if (defined($pid) && $pid==0) { 
    &run_child_code; 
    exit 0; 
} 

El proceso hijo ejecuta el bloque END {} ya que está saliendo, pero por lo general no quiero que eso ocurra. ¿Hay alguna manera de evitar que un proceso secundario llame al bloque END en la salida? Aparte de eso, hay una manera para un programa de "saber" que es un proceso hijo, por lo que podría decir algo como

END { unless (i_am_a_child_process()) { &some_cleanup_code } } 

?

Respuesta

7
use B; 
@{; eval { B::end_av->object_2svref } || [] } =(); 

pensé que había una Devel :: módulo que también le permiten hacer esto, pero no puedo encontrar ahora.

Por supuesto, no se puede de manera segura de hacer esto si está utilizando módulos arbitrarias que puedan estar utilizando bloques de extremo para sus propios fines ...


(edición haciendo OP) Usted puede conseguir control sobre los bloques END con B::end_av.Como prueba de concepto-:

END { print "This is the first end block.\n"; } 
my $END_block_2_line = __LINE__ + 1; 
END { print "This is the second end block.\n"; } 
END { print "This is the third end block.\n" } 

sub disable_specific_END_block { 
    use B; 
    my ($file, $line) = @_; 
    eval { 
    my @ENDs = B::end_av->ARRAY; 
    for (my $i=$#ENDs; $i>=0; $i--) { 
     my $cv = $ENDs[$i]; 
     if ($cv->START->file eq $file && $cv->START->line == $line) { 
     splice @{B::end_av->object_2svref}, $i, 1; 
     } 
    } 
    }; 
} 

disable_specific_END_block(__FILE__, $END_block_2_line); 


$ perl endblocks.pl 
This is the third end block. 
This is the first end block. 

Por lo general algo como esto podría ser excesiva para lo que necesito, pero puedo ver algunos casos en que esto sería muy útil.

+0

Quizás estabas pensando en 'Manip :: END'. – mob

+0

Sí, era Manip :: FIN. Estaba pensando en – ysth

18

No creo que haya ninguna manera de prevenir bloques de extremo se ejecute en un proceso en forma de horquilla, pero esto debe dejar que lo detecte: función

my $original_pid; BEGIN { $original_pid = $$ } 

... # Program goes here 

END { do_cleanup() if $$ == $original_pid } 
15

perldoc -f exit

La salida() no siempre sale de inmediato Llama a cualquier rutina END definida primero, pero estas rutinas END no pueden abortar la salida. Del mismo modo, todos los destructores de objeto que necesitan ser llamados se llaman antes de la salida real. Si esto es un problema, puede llamar al POSIX:_exit($status) para evitar el procesamiento de END y del destructor. Ver perlmod para más detalles.

+0

El truco de 'POSIX :: _ exit()' es ingenioso. – friedo

+2

El único problema es que debe atrapar _todas_ las posibles maneras en que el proceso secundario podría salir. Por ejemplo, si muere y no lo atrapas, ejecutará los bloques END. – cjm

+1

Además, es posible que desee ejecutar destructores de objetos, pero no los bloques END. – cjm

1

Simplemente configure algún tipo de indicador de acceso global, cuando ejecute run_child_code().

Hay varias maneras de hacerlo, pero probablemente incluiré esto en un paquete y usaré un paquete con formato léxico para manejar el almacenamiento. También podría usar un indicador de variable simple. Es un poco más simple. Me gusta la solución de paquete porque funcionará fácilmente en múltiples módulos.

END { &some_cleanup_code unless ImaKid->get; } 
my $pid = $fork; 
if(defined($pid) && $pid == 0) { 
    ImaKid->set; 
    &run_child_code; 
} 

BEGIN { 
    package ImaKid; 

    my $child_flag; 

    sub set { 
     $child_flag = 1; 
    } 

    sub get { 
     return $child_flag; 
    } 
} 

considere envolver el código de gestión de tenedor en el mismo paquete para crear una sola API consistente para todas sus necesidades relacionadas con la horquilla.

0

Sé que esto no es muy útil para usted, pero lo diré de todos modos: No utilice bloques END. Usualmente hay una mejor manera.

+0

mobrule podría no tener control sobre el código que usa bloques END – Leolo

+4

Los bloques END pueden ser bastante útiles. – Ether

4

Este es el código utilizado por POE :: Wheel :: Run para hacer justo lo que quiere. POE :: Kernel :: RUNNING_IN_HELL es verdadero cuando $^O eq 'MSWin32'. Adáptelo a sus necesidades.

sub _exit_child_any_way_we_can { 
    my $class = shift; 
    my $exitval = shift || 0; 

    # First make sure stdio are flushed. 
    close STDIN if defined fileno(STDIN); # Voodoo? 
    close STDOUT if defined fileno(STDOUT); 
    close STDERR if defined fileno(STDERR); 

    # On Windows, subprocesses run in separate threads. All the "fancy" 
    # methods act on entire processes, so they also exit the parent. 

    unless (POE::Kernel::RUNNING_IN_HELL) { 
    # Try to avoid triggering END blocks and object destructors. 
    eval { POSIX::_exit($exitval); }; 

    # TODO those methods will not exit with $exitval... what to do? 
    eval { CORE::kill KILL => $$; }; 
    eval { exec("$^X -e 0"); }; 
    } else { 
    eval { CORE::kill(KILL => $$); }; 

    # TODO Interestingly enough, the KILL is not enough to terminate this process... 
    # However, it *is* enough to stop execution of END blocks/etc 
    # So we will end up falling through to the exit($exitval) below 
    } 

    # Do what we must. 
    exit($exitval); 
} 
+1

Este autor parece haber descubierto que 'POSIX :: _ exit' no suprimirá los bloques' END' en Windows^H^H^H^H^H^H^H IN HELL. – mob

6

En una forma de hablar, la forma habitual de evitarlos en fork niños ED es con algo como:

exec "true"; 

O, si usted encuentra que demasiado poco fiable, estas todo el trabajo e incluso se utilice use strict compatible :

exec $^X => -eexit; 

exec ~~echo => ~~-echo; 

exec ~~echo => !!-echo; 

y la onomatopéyica

exec reverse reverse echo => ~~-echo; 

Rebmemer- En Perl, no hay tontos preguntas, pero puede haber tontas respuestas ...

$ perl -Mstrict '-leprint ucfirst lc reverse-Remember' 
+0

Lindo, aunque esto también tiene problemas con Windows. – mob

+0

@mobrule: ¿Quién no? Vivo en un mundo donde esa abominación no existe. Si eres un * Prisionero de $ Bill *, utiliza el método '$^X'. – tchrist

+0

Quiero decir que llamar a 'exec' desde un proceso hijo no evita los bloques' END' en Windows, incluyendo 'exec' a' $^X'. – mob

0

Esto es lo que hice. al comienzo del programa (donde sé que aún no ha pasado ninguna bifurcación) Almacene el PID. my $ parent_pid = $$;

#Then in the end block 
END { 
    my $current_pid = $$; 
    if ($parent_pid == $current_id) 
     ##Here goes my cleanup code. 
    } 
} 
Cuestiones relacionadas