2011-02-04 15 views
5

Me gustaría detectar si mi objeto está siendo DESTROY 'como parte de la destrucción global, e imprimir una advertencia (ya que eso claramente sería un error y conduciría a la pérdida de datos). La manera obvia de hacer eso parecería ser:Detectando la destrucción global en Perl

sub DESTROY { 
    my $self = shift; 
    # ⋮ 
    if (i_am_in_global_destruction()) { 
     warn "I survived until global destruction"; 
    } 
} 

pero he sido incapaz de encontrar una buena manera de detectar la destrucción global (en lugar de golpear a la normalidad refcount 0 destrucción).

Por "buen sentido", quiero decir que no esta, que aunque funciona en 5.10.1 y 5.8.8, probablemente rompe la segunda a alguien le da una mirada extraña:

sub DESTROY { 
    $in_gd = 0; 
    { 
     local $SIG{__WARN__} = sub { $_[0] =~ /during global destruction\.$/ and $in_gd = 1 }; 
     warn "look, a warning"; 
    } 
    if ($in_gd) { 
     warn "I survived until global destruction"; 
    } 
}' 
+0

¿Por qué no simplemente guardar el contenido de su objeto cuando se DESTRUYE, y no preocuparse de si es durante la destrucción global o no? – Ether

+0

@Ether: Porque el orden de destrucción global no está definido, y necesito otros objetos para salvar el mío. – derobert

+3

Veo (de la fuente 'Devel :: GlobalDestruction') que en v5.13.7 hay un [' $ {^ GLOBAL_PHASE} '] (http://search.cpan.org/~jesse/perl-5.13.9 /pod/perl5137delta.pod#New_global_variable_${^GLOBAL_PHASE}) variable que se adapta a este propósito. – mob

Respuesta

10

Hay un módulo de Devel::GlobalDestruction que usa un poco de XS para permitirte llegar directamente a la bandera de destrucción global.

Actualización: desde perl 5.14.0 hay una variable global ${^GLOBAL_PHASE} que se establece en "DESTRUCT" durante la destrucción global. En general, aún debe usar Devel :: GlobalDestruction, ya que funciona con Perls de nuevo a 5.6. Cuando se instala en un perl con ${^GLOBAL_PHASE}, utilizará la función incorporada y ni siquiera necesitará un compilador de C para compilar.

+2

¡Gracias! E incluso está empaquetado para Debian como 'libdevel-globaldestruction-perl', ya que Lenny no menos. – derobert

8

Una solución que es lo suficientemente buena para mí es establecer un indicador en un bloque END.

package Whatever; 
our $_IN_GLOBAL_DESTRUCTION = 0; 
END { 
    $_IN_GLOBAL_DESTRUCTION = 1; 
} 
+0

Eso no necesariamente funcionará, ya que otros paquetes 'END bloques pueden ejecutarse antes que ese. – Ether

+3

Además, la destrucción global se produce técnicamente después de que se completen todos los bloques END, por lo que el peor caso es que podría marcar algo incorrectamente mientras se destruye en otro bloque END. – mob

Cuestiones relacionadas