スレッドに対してSIGALRMを送る方法は?
SIGALRM - rubyco(るびこ)の日記より。プログラミング言語 Ruby リファレンスマニュアルを読んだ感じでは、シグナルを送る先はRuby自身のプロセスであり、一スレッドにのみSIGALRMを送ることは出来なさそう。Thread.killの例にも
とあった。Process.kill :QUIT, $$ # 自身にSIGQUITを送信
trap("ALRM") { puts("ALRM") } sleep puts("okita?")
というスクリプトを実行したRubyのプロセスに対してkill -ALRMを行って「ALRM」と「okita?」の二つが表示されて終了することを確認した上で、以下のようなスクリプトを試してみた。
trap("ALRM") { puts("ALRM") } th = Thread.start do Thread.pass sleep puts("okita?") end th.join
sleepしたスレッドが起きたならば「okita?」と表示される、のを想定してみた。実際にはSIGALRMを送っても「ALRM」と表示されるだけでputs("okita?")
は実行されないままスレッドが生き続けていた。もしかしてメインスレッドとその他のスレッドで挙動が異なるとか?
[ruby-dev:2272] Re: [Problem] thread and signal and sleepを読むと「ハンドラが設定されていないSIGINTだけはmain_threadが受け取ります.それ以外は不定です
」とあった。おそらく「シグナルを受け取るスレッドはつねに一つで(全てのスレッドでいっせいに同じシグナルを受け取るわけではない)、そのスレッドはたまたま受けた瞬間に実行中であるスレッド(カレントスレッド)である」かなあ。これが現在の実装でも同じだとすると「たまたまsleepしているスレッドでは受け取れなかった」とも言えそうだけど、それにしてはkill -ALRMをかなり連続で行ってみても結果が変わらないのは謎。「sleepしているスレッドはシグナルを受け取らない」もしくは「メインスレッドでのみシグナルを受け取る」なんてことが無い限り、何らかのタイミングでシグナルを受け取ってくれても良さそうなのに(私の手が遅い、または運が悪い、という可能性もあり)。
SIGALRMをトラップする、またはメインスレッドでSignalException例外を受けて、Thread#wakeupを使って起こす、というのでは疑問の直接的な解決(sleepの説明の理解)には至らないような気もする。
irb(main):001:0> th = Thread.start do irb(main):002:1* Thread.pass irb(main):003:1> sleep irb(main):004:1> puts("okita?") irb(main):005:1> end => #<Thread:0x813c888 run> irb(main):006:0> trap("ALRM") do irb(main):007:1* th.wakeup irb(main):008:1> end => "DEFAULT" irb(main):009:0> th.join (ここで他のターミナルからkill -ALRMしてみる) okita? => #<Thread:0x813c888 dead>