alarm

SIGALRM をスケジューリングする

構文

解説

alarm は、SECONDS に指定した秒数が経過したら自身のプロセスに SIGALRM を発生させます。 これは一種のタイマーとして機能します。 SECONDS が指定されなかったら、$_ が使われます。 タイマーは一つしかセットすることができません。 すでにタイマーが存在するのにも関わらず、再度 alarm を呼び出すと、 以前のタイマーはキャンセルされ、新たなタイマーがセットされることになります。 以前のタイマーをキャンセルするには、SECONDS に 0 を指定します。

alarm はよく時間がかかりそうな処理のタイムアウトの実装に使われます。 次のサンプルコードは、時間がかかりそうな処理に対して 5 秒のタイムアウトを設けています。

eval {
    # ALRM シグナルをキャッチしたら die して eval を抜ける
    local $SIG{ALRM} = sub { die "alarm\n" };

    # ALRM シグナルを 5 秒後に発するよう設定
    alarm 5;

    # 時間がかかりそうな処理を実行
    &do_heavy_something();

    # ALRM シグナルを解除
    alarm 0;
};

if ($@) {
    # ALRM シグナルによってタイムアウトを検知した場合
    if ( $@ eq "alarm\n" ) {
        print "タイムアウトしました。";
    }

    # &do_heavy_something() で例外が発生した場合
    else {
        print "処理に失敗しました: $@\n";
    }
}

細かい点ですが、die に指定するメッセージの最後に "\n" を加えている点に注意してください。 もし最後に "\n" を加えないと、 die はエラーを出力する際に "alarm at ./sample.pl line 6." のように、 ファイル名や行番号の情報を付加してしまいます。 $@ の値を評価する際の邪魔になりますので、意図的に die に引き渡すメッセージの最後に "\n" を加えているのです。

1 秒未満の精度でタイマーを作りたい場合は、 Time::HiRes モジュールの ualarm を使うと良いでしょう。 ualarm ならマイクロ秒の単位でタイムアウトを指定することができます。 Time::HiRes は Perl 5.8 から標準モジュールになっています。