Detecting global destruction in Perl
Asked Answered
A

2

6

I'd like to detect if my object is being DESTROY'd as part of global destruction, and print out a warning (as that'd clearly be an error and lead to data loss). The obvious way to do that would seem to be:

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

but I have been unable to find a good way to detect global destruction (instead of normal refcount hit 0 destruction).

By "good way", I mean not this, which though it works on 5.10.1 and 5.8.8, probably breaks the second someone gives it an odd glance:

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";
    }
}'
Amontillado answered 4/2, 2011 at 20:43 Comment(4)
Why not simply save your object contents when it is DESTROYed, and not worry about whether it is during global destruction or not?Zabaglione
@Ether: Because the order of global destruction is undefined, and I need other objects to save mine.Amontillado
I see (from the Devel::GlobalDestruction source) that in v5.13.7 there is a ${^GLOBAL_PHASE} variable that suits this purpose.Chaddie
See also: search.cpan.org/~jesse/perl-5.13.9/pod/perlvar.pod#GLOBAL_PHASEChaddie
E
12

There's a module Devel::GlobalDestruction that uses a tiny bit of XS to let you get at the global destruction flag directly.

Update: since perl 5.14.0 there is a global variable ${^GLOBAL_PHASE} that will be set to "DESTRUCT" during global destruction. You should still generally use Devel::GlobalDestruction, since it works with perls back to 5.6. When installing on a perl with ${^GLOBAL_PHASE} it will use the built-in feature and not even require a C compiler to build.

Eba answered 4/2, 2011 at 20:48 Comment(1)
Thank you! And it's even packaged for Debian as libdevel-globaldestruction-perl, since Lenny no less.Amontillado
C
8

A solution that is good enough for me is to set a flag in an END block.

package Whatever;
our $_IN_GLOBAL_DESTRUCTION = 0;
END {
    $_IN_GLOBAL_DESTRUCTION = 1;
}
Chaddie answered 4/2, 2011 at 21:4 Comment(2)
That won't necessarily work, as other packages' END blocks may get executed before that one is.Zabaglione
Also, global destruction technically occurs after all the END blocks are completed -- so the worst case here is that it could flag something incorrectly while it's being destroyed in another END block.Chaddie

© 2022 - 2024 — McMap. All rights reserved.