这种非分叉的Perl超时方法是否安全?

时间:2012-06-13 05:42:25

标签: perl timeout

我有一个函数可以调用其他几个函数,例如

sub do_work {
    send_mail();
    send_soap_envelope();
    send_rpc();
}

被调用的函数可能会挂起,所以我希望在超时后停止它们。我想避免分叉,因为它在我的上下文中很昂贵(例如,在每个fork之后需要重新创建数据库句柄)。我想出了以下方案:

sub timeout {
    my ($code) = @_;
    eval {
        alarm 2;
        local $SIG{ALRM} = sub { die 'timeout' };
        &$code;
        alarm 0;
    };
    # handling of $@ eq 'timeout' removed for brevity
}

sub do_work {
    timeout \&send_mail;
    timeout \&send_soap_envelope;
    timeout \&send_rpc;
};

timeout()函数(在此示例中硬编码为超时2秒)使用eval块作为使用die中止有效载荷函数执行的方法。

这在我的测试场景中运行良好,但是如果die在Perl解释器未处于“安全状态”时中断有效负载功能,我会感到不安。而它正在处理一个XS子程序。我的直觉是对的吗?

3 个答案:

答案 0 :(得分:5)

自5.8.1以来,Perl使用“安全信号处理”。它不会将信号处理程序提供给系统,而是提供安全的信号处理程序。这个安全的信号处理程序只是注意到接收到信号并返回。在执行Perl操作码之间,解释器检查是否收到了任何信号,如果存在,则调用信号处理程序。

这意味着在长操作中间不会处理信号,例如长XS调用或长正则表达式匹配。信号中断大多数系统调用,因此如果您处于阻塞系统调用中(例如sleepread等),信号进入后不久将调用您的信号处理程序

alarm(2);
my $s = time;
$SIG{ALRM} = sub {
   my $e = time;
   print $e-$s, "\n";  # 6, not 2.
};
('a' x 25) =~ /a*a*a*a*a*a*a*a*a*(?:b|c)/;

* - 为了加快程序的速度,现在检查的次数会少一些,但你不应该注意到它们之间的区别。

答案 1 :(得分:1)

它不是完全安全,因为它在安装alarm()处理程序之前调用SIGALRM。交换local $SIG{ALRM}alarm行,应该会有很大的改进。

答案 2 :(得分:0)

好的,现在我看到perldoc -f alarm提到我确切的用例:

  

如果您想使用“闹钟”超时系统调用,则需要使用“eval”/“die”对。

(其后是示例代码。)