みんつです.

	先日発生しました本件,原因らしきものがつかめました.
	直接 IO::select() の問題ではなかったようです.
	
	プログラムの考慮不足が発端ではあるのですが,プログラムを
	簡単にして紹介すると,

	t = telnet.new()

	begin
		t.login() { |c| print c }

		print( ">>> SENDING\n" )
		t.cmd( "String" => "実行コマンド" ) { |c| print c }
	ensure
		print( ">>> CLOSING\n" )
		t.cmd( "String" => "exit", "Match" => // ) { |c| print c }
		t.close
	end

	のようになってしまっていました.この状態で

>> |	切替元サーバは telnet コネクションが継続してるとみなして,
>> |	データを送って来ているのですが,telnet クライアントはアド
>> |	レスが変わった切替先のサーバに応答をしています.
>> |
>> |	切替元 [データ] --> Net::telnet --> [ACK] 切替先
>> |	                                 <-- [RST]
>> |	    [ 再送 ] -->             --> [ACK]
>> |	                                 <-- [RST]

	のような事が実行コマンドの実行中に起きると,正しく 
	Errno:: ECONNRESET が net/telnet.rb:554 で raise さ
	れています.

	しかし,プログラムは ensure 節を実行して抜けようとする
	ため「例外送出後の不定な状態」で "exit" を送信しようと
	してしまいます.(これはプログラムの考慮不足)

	しかし,この後,ruby 本体が無期限(struct timeout * = 0)
	の select() を呼び出してしまい,永遠に待ち続けスレッドも
	止まることになってしまいます.

	# ソケットそのものは存続しているはずであるので,他のエラー
	# にはならない?! しかし,blocking select になってしまう
	# のもなぜかはちょっと気になります.

	syscall の alarm でプログラムの方はすでに解決させてしま
	っておりますが,

	とりあえず,お知らせとお礼までに.

なお,以下はトレースです.

	eval.c の rb_thread_select() の select() の前にデバッグ
	文を以下のように入れ,

TRAP_BEG;
if ( ! tvp ) {
	fprintf( stderr, "*** %d select : blocked\n", __LINE__ );
} else {
	fprintf( stderr, "*** %d select : s=%ld u=%ld\n", __LINE__, tvp->tv_sec, tvp->tv_usec );
}
n = select(max, read, write, except, tvp);
TRAP_END;

	実行してみると,
	(プログラム名 test_client.rb タイムアウトは 600秒です)

$ ./test_client.rb
Trying 192.168.0.224...
Connected to 192.168.0.224.

*** 10769 select : s=600 u=0
(この間略)
*** 10769 select : s=600 u=0
login: root
*** 10769 select : s=600 u=0
*** 10769 select : s=0 u=0
*** 10767 select : blocked
*** 10769 select : s=600 u=0
Password:
*** 10769 select : s=600 u=0
Last login: Tue Aug  1 15:16:49 from 192.168.0.200
Sun Microsystems Inc.   SunOS 5.10      Generic January 2005
*** 10769 select : s=0 u=0
#
>>> SENDING(login 後のコマンド送信)
*** 10767 select : blocked
*** 10769 select : s=600 u=0
実行コマンド(のエコーバック)
*** 10769 select : s=600 u=0
*** 10769 select : s=600 u=0
(この間,実行コマンドの結果が帰っている)
	...
(結果が流れている間にサーバを切替える)
*** 10769 select : s=600 u=0
Exception `Errno::ECONNRESET' at /usr/local/lib/ruby/1.8/net/telnet.rb:554 -
Connection reset by peer
>>> CLOSING(ensure 節に入った)
*** 10767 select : blocked
^C(無限待ちになっているので,ctrl-C で止めた)
/usr/local/ruby/1.8/net/telnet.rb:599:in `select': Interrupt
from /usr/local/lib/ruby/1.8/net/telnet.rb:599:in `write'
from /usr/local/lib/ruby/1.8/net/telnet.rb:622:in `print'
from /usr/local/lib/ruby/1.8/net/telnet.rb:634:in `puts'
from /usr/local/lib/ruby/1.8/net/telnet.rb:676:in `cmd'
from ./test_client.rb:29

-- 
みんつ - minz / minz.org - みぎまつひろし