2011-02-04 3 views
5

Vorrei rilevare se il mio oggetto è DESTROY 'd come parte della distruzione globale e stampare un avviso (in quanto sarebbe chiaramente un errore e portare alla perdita di dati). Il modo più ovvio per farlo sembrerebbe essere:Rilevamento della distruzione globale in Perl

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

ma sono stato in grado di trovare un buon modo per rilevare distruzione globale (al posto del normale refcount colpito 0 distruzione).

Per "buon modo", non intendo questo, che se funziona sul 5.10.1 e 5.8.8, probabilmente rompe il secondo qualcuno dà uno sguardo strano:

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

Perché non salvare semplicemente il contenuto dell'oggetto quando è DISTRUTTO, e non preoccuparsi se è durante la distruzione globale o no? – Ether

+0

@Ether: Perché l'ordine di distruzione globale non è definito e ho bisogno di altri oggetti per salvare il mio. – derobert

+3

Vedo (dal sorgente 'Devel :: GlobalDestruction') che nella v5.13.7 c'è un [' $ {^ GLOBAL_PHASE} '] (http://search.cpan.org/~jesse/perl-5.13.9 /pod/perl5137delta.pod#New_global_variable_${=GLOBAL_PHASE}) variabile adatta a questo scopo. – mob

risposta

10

C'è un modulo Devel::GlobalDestruction che usa un piccolo bit di XS per farti arrivare direttamente alla bandiera di distruzione globale.

Aggiornamento: dal perl 5.14.0 v'è una variabile globale ${^GLOBAL_PHASE} che sarà impostato su "DESTRUCT" durante la distruzione globale. In generale dovresti comunque utilizzare Devel :: GlobalDestruction, poiché funziona con perls di nuovo a 5.6. Quando si installa su perl con ${^GLOBAL_PHASE} userà la funzione integrata e non richiederà nemmeno la compilazione del compilatore C.

+2

Grazie! Ed è persino confezionato per Debian come 'libdevel-globaldestruction-perl', dal momento che Lenny non meno. – derobert

8

Una soluzione che è abbastanza buona per me è impostare un flag in un blocco END.

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

Non funzionerà necessariamente, poiché i blocchi END degli altri pacchetti potrebbero essere eseguiti prima di quello. – Ether

+3

Inoltre, la distruzione globale si verifica tecnicamente dopo che tutti i blocchi END sono stati completati, quindi il caso peggiore qui è che potrebbe segnalare qualcosa in modo errato mentre viene distrutto in un altro blocco END. – mob