From: Yui NARUSE Date: 2010-07-02T12:20:43+09:00 Subject: [ruby-dev:41778] [Bug #3515] FreeBSD wrongly raises ECONNRESET on close(2) Bug #3515: FreeBSD wrongly raises ECONNRESET on close(2) http://redmine.ruby-lang.org/issues/show/3515 起票者: Yui NARUSE ステータス: Open, 優先度: Normal カテゴリ: core ruby -v: ruby 1.9.3dev (2010-07-02 trunk 28520) [x86_64-freebsd8.1] FreeBSD 8 では現在以下のようなテストに失敗しています。 1) Error: test_idle(IMAPTest): Errno::ECONNRESET: Connection reset by peer /home/naruse/ruby/test/net/imap/test_imap.rb:189:in `test_idle' 2) Failure: test_03(TestDRbSSLCore) [/home/naruse/ruby/test/drb/drbtest.rb:138]: [DRb::DRbConnError] exception expected, not Class: Message: <"Connection reset by peer"> ---Backtrace--- /home/naruse/ruby/test/drb/drbtest.rb:139:in `block in test_03' /home/naruse/ruby/test/drb/drbtest.rb:138:in `test_03' --------------- 3) Failure: test_07_public_private_protected_missing(TestDRbSSLCore) [/home/naruse/ruby/test/drb/drbtest.rb:182]: Exception raised: <#>. これらに共通するのは「Errno::ECONNRESET: Connection reset by peer」という例外が発生している点です。 この例外は socket の close(2) を呼んだ際に errno に ECONNRESET がセットされたときに発生します。 しかし、この挙動は POSIX 仕様外であり、FreeBSD 独自のものです。 http://www.freebsd.org/cgi/man.cgi?query=close&apropos=0&sektion=0&manpath=FreeBSD+8.0-RELEASE&format=html http://www.opengroup.org/onlinepubs/9699919799/functions/close.html http://netbsd.gw.com/cgi-bin/man-cgi?close++NetBSD-current http://www.openbsd.org/cgi-bin/man.cgi?query=close&apropos=0&sektion=0&manpath=OpenBSD+Current&arch=i386&format=html http://leaf.dragonflybsd.org/cgi/web-man?command=close§ion=ANY http://www.kernel.org/doc/man-pages/online/pages/man2/close.2.html http://developer.apple.com/mac/library/documentation/Darwin/Reference/ManPages/man2/close.2.html これが結果的に、他の OS では例外が投げられない状況で例外が発生するという現象を生み出しています。 以下は関連する議論です。 http://old.nabble.com/close()-failing-with-ECONNRESET-td28817716.html http://old.nabble.com/Re:-kern-146845:--libc--close(2)-returns-error-54-(connection-reset-by-peer)-wrongly-td28649525.html で、Ruby における対策ですが、close(2) で errno に ECONNRESET がセットされた場合、 それを無視するべきだと思います。 いかがそのパッチなのですがいかがでしょうか。 diff --git a/io.c b/io.c index 05b2d45..a1b49d2 100644 --- a/io.c +++ b/io.c @@ -3436,7 +3436,7 @@ fptr_finalize(rb_io_t *fptr, int noraise) /* fptr->fd may be closed even if close fails. * POSIX doesn't specify it. * We assumes it is closed. */ - if (close(fptr->fd) < 0 && NIL_P(err)) + if (close(fptr->fd) < 0 && NIL_P(err) && errno != ECONNRESET) err = noraise ? Qtrue : INT2NUM(errno); } skip_fd_close: ---------------------------------------- http://redmine.ruby-lang.org