ruby-trunk-changes r35631 - r35639

今日は拡張ライブラリ etc の不具合(ファイルリソースのリーク)修正、テストの修正、Enumerator::Lazy#take の修正などがありました。

luislavena:r35631 2012-05-13 06:46:51 +0900

test/ruby/test_io.rb のテストで IO.for_fd で生成した IO オブジェクトの close で不正な file descriptor を持つ IO ができてしまって、後続のテストが走りはじめてから使用中の file descriptor を close してしまってエラーになる現象の対策として、全てのテストの setup/teardown で GC.disable と GC.enable を呼ぶようにしています。 [ruby-core:43951][Bug #6228]
コミットログにわたしの名前が上がっていて、以前このチケットをみかけて心当たりがあったのでパッチを貼っておいたのですが(http://bugs.ruby-lang.org/issues/6228#note-12)、修正内容はちょっと違うようです。既にチケットでもこの修正方法はやりすぎだと指摘されているので revert されるんじゃないかと思います。パッチ投げたっきりすっかり忘れてました……。というか GC.disable すると肝心の test_flush_in_finalizer1 が IO が回収されなくてテストにならないどころか "Too many open files" でエラーになってしまいます。
解説しておくと、test_flush_in_finalizer1 というテストは [Bug #3910] のために IO オブジェクトが finalizer で close される時にエラーがおきないことをチェックするため、たくさんファイルを開いて開きっぱなしにしています。で、おそらくたくさん IO オブジェクトを生成しているのでそれをちゃんと閉じようとしているのだと思うのですが ensure 節で IO.for_fd を使って開いた IO オブジェクトの file descriptor を直接閉じようとしています。しかしこれだとテスト本体で open した IO オブジェクトのうちまだ GC で回収 & finalizer実行されていないものを閉じてしまうので、無効な file descriptor を持った IO オブジェクトが未回収の状態で存在してしまいます。file descriptor は再利用されるので後続のテストでその file descrptor を open する可能性があり、その状態で GC が走って、今度は無効な file descriptor を持った IO オブジェクトが回収されると、再利用されている file descriptor を自分が open したものだと思って close してしまうので、まだ利用中の IO オブジェクトの file descriptor が閉じられてしまって Errno::EBADF が発生するという次第です。ensure での IO.for_fd を使った close はおそらく IO をたくさん作るのでここで回収しておこうという配慮だと思うのですが、このような問題があるのでここは単に GC.start して回収できるぶんは回収しておけばそれでいいんじゃないかと思って書いたのが投稿したパッチです。
と、いうことをちゃんと英語で説明できていたらよかったんですけど。mame さんが comment:14で補足して下さったのでこれで大丈夫だろうと思って終わった気になってしまってました。

svn:r35632 2012-05-13 06:46:55 +0900

version.h の日付更新。

tadf:r35633 2012-05-13 17:39:18 +0900

ext/date/date_strftime.c のリファクタリング。ほぼ同じことをしている "%a" と "%A" や "%b" と "%B" の処理をまとめたり全体的に case 節の順番をアルファベット順に入れ替えたりといったことをしているようです。

tadf:r35634 2012-05-13 18:14:46 +0900

ext/date/date_strftime.c の不要なコードをばっさり削除してコードのスタイルも変更しているようです。結構不要な部分があったようですね。単体テスト用の main 関数まであったとは。

akr:r35635 2012-05-13 23:00:16 +0900

ext/etc/etc.c で setpwent(3) や setgrent(3) で開いた /etc/passwd や /etc/group ファイルを閉じるために endpwent()/endgrent() をそれぞれ呼ばないといけないのですが、ensure の中で呼んでいなかったためブロックで例外が発生するときちんと後始末ができていなかったので、endpwent()/endgrent() の呼び出しを ensure 処理に移動しています。

nagachika:r35636 2012-05-13 23:47:29 +0900

r35631 のところで解説したように、テストの意図に反しているのと、このせいで test_io.rb がエラーになってしまうので r35631 を revert しています。 [ruby-core:43951] [Bug #6228]

nagachika:r35637 2012-05-13 23:50:49 +0900

改めて test_flush_in_finalizer1 の ensure で IO.for_fd を使って IO を閉じようとするのはやめて、GC.start を呼んで回収できるぶんだけ回収するだけにしています。 [ruby-core:45020] [Bug #6228]

nagachika:r35638 2012-05-14 00:24:40 +0900

Enumerator::Lazy#take で lazy 版 take の Lazy を作ってそれに force/to_a メソッドを複数回呼ぶと、2回目以降は take の引数の個数で打ち切られずに全ての要素がかえってきていたので、2回目以降も同じ個数だけ返すようにしました。 [ruby-dev:45634] [Bug #6428]

svn:r35639 2012-05-14 00:24:45 +0900

version.h の日付更新。