[#41778] [Bug #3515] FreeBSD wrongly raises ECONNRESET on close(2) — Yui NARUSE <redmine@...>

Bug #3515: FreeBSD wrongly raises ECONNRESET on close(2)

12 messages 2010/07/02

[ruby-dev:41778] [Bug #3515] FreeBSD wrongly raises ECONNRESET on close(2)

From: Yui NARUSE <redmine@...>
Date: 2010-07-02 03:20:43 UTC
List: ruby-dev #41778
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: <Errno::ECONNRESET>
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>>.

これらに共通するのは「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&section=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

In This Thread

Prev Next