【问题标题】:Why is `$@` untrustworthy?为什么`$@` 不可信?
【发布时间】:2010-09-10 17:36:15
【问题描述】:

我似乎记得在eval 之后信任$@ 的值是不安全的。关于信号处理程序有机会在您看到它或其他东西之前设置$@。我现在也太累太懒了,找不到真正的原因。那么,为什么信任$@ 不安全?

【问题讨论】:

标签: perl exception eval race-condition


【解决方案1】:

Try::Tiny perldoc 对$@ 的问题进行了明确的讨论:

eval 存在许多问题。

破坏 $@

当您运行 eval 块并且它成功时,$@ 将被清除,可能会破坏当前正在捕获的错误。

这会导致远程操作,清除调用者可能尚未处理的先前错误。

$@ 必须在调用 eval 之前正确本地化以避免此问题。

更具体地说,$@ 在 eval 开始时被破坏,这也使得在你死之前无法捕获先前的错误(例如,当使用错误堆栈创建异常对象时)。

因此,try 实际上会在 eval 块的开头将 $@ 设置为其先前的值(本地化之前)。

本地化 $@ 以静默方式掩盖错误

在 eval 块中 die 的行为类似于:

sub die {
        $@ = $_[0];
        return_undef_from_eval();
}

这意味着,如果你有礼貌并且本地化 $@,你就不能在那个范围内死掉,否则你的错误将被丢弃(改为打印“Something's wrong”)。

解决方法非常难看:

my $error = do {
        local $@;
        eval { ... };
        $@;
};

...
die $error;

$@ 可能不是真实值

这段代码是错误的:

if ( $@ ) {
        ...
}

因为之前的警告可能未设置。

$@ 也可能是一个重载的错误对象,其计算结果为 false,但无论如何这都是在自找麻烦。

经典的故障模式是:

sub Object::DESTROY {
        eval { ... }
}

eval {
        my $obj = Object->new;

        die "foo";
};

if ( $@ ) {

}

在这种情况下,由于 Object::DESTROY 没有本地化 $@,但仍然使用 eval,它会将 $@ 设置为 ""。

当堆栈展开时调用析构函数,在 die 将 $@ 设置为“foo at Foo.pm line 42\n”之后,因此当 if ( $@ ) 被评估时,它已被 eval 中的析构函数。

解决方法比以前的更难看。即使我们无法从未本地化的代码中保存 $@ 的值,我们至少可以确定 eval 由于错误而中止:

my $failed = not eval {
        ...

        return 1;
};

这是因为抓到骰子的 eval 总是会返回一个假值。

【讨论】:

  • 是的,我想这就是我所记得的。
  • 别再玩那些疯狂的游戏了。
【解决方案2】:

Try::Tiny docs 有一个很好的 eval/$@ 缺点列表。我想你可能指的是那里的Localizing $@ silently masks errors 部分。

【讨论】:

    【解决方案3】:

    $@ 与每个全局变量都有相同的问题:当其他东西设置它时,它会在整个程序中重置。任何eval 都可以设置$@。即使您在附近看不到eval,您也不知道还有谁会调用它(子例程、绑定变量等)。

    【讨论】:

      猜你喜欢
      • 2018-01-28
      • 2017-03-16
      • 1970-01-01
      • 1970-01-01
      • 2011-06-04
      • 2014-03-04
      • 2016-06-07
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多