ruby-trunk-changes r35625 - r35629

今日は require などでファイルロード中のシグナル受信でエラーになる可能性があった不具合修正などがありました。

naruse:r35625 2012-05-12 05:39:35 +0900

test/ruby/test_signal.rb の test_signal_requiring というテストが require 中にシグナルを受信した時の挙動をテストするために生成した子プロセスが本来シグナルを送信したい require したファイル内の sleep ではなくてその前にファイルを load しようとした時の IO 待ちのタイミングでシグナルを受信すると失敗してしまうという問題に対する対応です。現象についてコミットログにとても詳細に書かれているので訳します。

(1) プロセスが lex_io_gets() -> rb_thread_internal() -> rb_thread_io_blocking_region() と呼び出ししているタイミングでシグナルを受信する
(2) rb_threadptr_execute_interrupts_common() で th->errinfo に INT2FIX(TAG_FATAL) をセットする。(訳注: rb_thread_t::errinfo には Qnil, 例外オブジェクト、および fatal エラー(Fixnum)や break, 大域脱出(throw)などのフロー制御用の情報(NODE)が入ります)
(3) rb_load_file() から rb_load_internal() の EXEC_TAG() までジャンプします(訳注: EXEC_TAG() は ruby で言う rescue している点みたいなもので C 言語としては setjmp/longjmp でマシンスタックを一気に巻き戻して戻ります)。このとき非0を返すので loaded を TRUE にセットすることなく処理が進みます。
(4) loaded が FALSE のままなので rb_exc_raise(GET_THREAD()->errinfo) を呼ぶ。しかしこの時点で th->errinfo に Fixnum(INT2FIX(TAG_FATAL))が格納されているので SEGVする。

というわけで rb_load_internal() で rb_exc_raise() している所で errinfo が Fixnum だったら例外発生はしないようにしています。
詳細で事情がよくわかるコミットログでした。すばらしいです。
ここで TAG_FATAL をスルーしてもすぐ下で EXEC_TAG() の戻り値の state をチェックして再度フロー制御関係の処理をさせているので fatal エラーは伝播します。

load.c のこの周辺を読むと、修正した rb_exc_raise() の下に上にも書いたフロー制御関係の rb_vm_jump_tag_but_local_jump() を呼んで、その後再度 rb_exc_raise(th->errinfo) を呼ぶ分岐があります。上の rb_exc_raise() はファイルを読み込んでパースして AST にするところまでで例外が発生した場合のためのようなのですが、これを後のものとまとめてしまっていいんじゃないでしょうか。

drbrain:r35626 2012-05-12 06:02:58 +0900

FileUtils.cp_r の rdoc のサンプルコードの説明が間違っていたのを修正しています。 [ruby-core:44941] [Bug #6411]

drbrain:r35628 2012-05-12 07:00:43 +0900

sdbm の rdoc を追加しています。[ruby-core:44932] [Bug #6410]

drbrain:r35629 2012-05-12 07:10:43 +0900

r35626 の ChangeLog のエントリでチケット番号を間違っていたのを修正しています。