在Perl中检测全局销毁

6

我希望能够检测到我的对象是否在全局销毁的过程中被DESTROY,并打印出警告信息(因为这显然是一个错误,并会导致数据丢失)。看起来最明显的方法是:

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

但是我一直无法找到一个好的方法来检测全局销毁(而不是正常的引用计数为0销毁)。

所谓“好的方法”,我的意思是不是这个,虽然它在5.10.1和5.8.8上可以工作,但很可能在有人对它进行奇怪的注视时就会崩溃:

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";
    }
}'

为什么不在对象被销毁时直接保存其内容,而不必担心它是在全局销毁期间还是其他时间被销毁的呢? - Ether
@Ether:因为全局销毁的顺序是未定义的,而我需要其他对象来保存我的对象。 - derobert
3
我从Devel::GlobalDestruction源代码中看到,在v5.13.7中有一个${^GLOBAL_PHASE}变量(http://search.cpan.org/~jesse/perl-5.13.9/pod/perl5137delta.pod#New_global_variable_${^GLOBAL_PHASE}),非常适合这个目的。 - mob
请参考:http://search.cpan.org/~jesse/perl-5.13.9/pod/perlvar.pod#GLOBAL_PHASE - mob
2个回答

12

有一个模块Devel::GlobalDestruction使用了一点点XS,让你可以直接获取全局销毁标志。

更新:自从perl 5.14.0以来,有一个全局变量${^GLOBAL_PHASE}会在全局销毁期间被设置为"DESTRUCT"。通常仍应该使用Devel::GlobalDestruction,因为它适用于Perl 5.6及以后的版本。当在安装有${^GLOBAL_PHASE}的Perl上安装时,它将使用内置功能,甚至不需要C编译器即可构建。


2
谢谢!并且它甚至为Debian打包成了 libdevel-globaldestruction-perl,自Lenny以来。 - derobert

8
对我来说足够好的解决方案是在END块中设置一个标志。
package Whatever;
our $_IN_GLOBAL_DESTRUCTION = 0;
END {
    $_IN_GLOBAL_DESTRUCTION = 1;
}

这并不一定有效,因为其他软件包的END块可能会在那个块之前执行。 - Ether
4
全球性的破坏在所有的 END 块完成后才会技术上发生--因此,最糟糕的情况是,在另一个 END 块中被销毁时它可能会错误地标记某些内容。 - mob

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接