From: Daniel Martin Date: 2007-01-24T13:22:43+09:00 Subject: Re: Does Ruby's FTP lib have a ghostly bug? "S. Robert James" writes: > (Here is a stack trace: > # > "/ruby/lib/ruby/1.8/net/ftp.rb:211:in `readline'" > "/ruby/lib/ruby/1.8/net/ftp.rb:211:in `getline'" > "/ruby/lib/ruby/1.8/net/ftp.rb:221:in `getmultiline'" > "/ruby/lib/ruby/1.8/net/ftp.rb:235:in `getresp'" > "/ruby/lib/ruby/1.8/net/ftp.rb:251:in `voidresp'" > "/ruby/lib/ruby/1.8/net/ftp.rb:176:in `connect'" > "/ruby/lib/ruby/1.8/monitor.rb:229:in `synchronize'" > "/ruby/lib/ruby/1.8/net/ftp.rb:174:in `connect'" > "/ruby/lib/ruby/1.8/net/ftp.rb:136:in `initialize'" > "/ruby/lib/ruby/1.8/net/ftp.rb:120:in `open'" > "my_code/connection.rb:27:in `ftp'" # Net::FTP.open(ftp_host, > username, password) Looking at that, and at other messages in this thread, I tried this experiment. First, I set up a service locally that listened to port 8989 and simply echoed ten lines with a five second delay between each echo. (with a simple shell script + inetd) Then, in irb: require "socket" (1..1300).map{|s|puts s;a=TCPSocket.open("localhost",8989);a.readline;a} This fails with the same EOFError reported in ftp.rb, after opening the 256th socket. (I use "map" instead of "each" to keep from closing sockets in garbage collection) It would seem that somehow when the underlying operating system has too many descriptors open, the effect is that TCPSocket.open succeeds, but it manages to succeed in such a manner that .readline on the resulting socket will immediately trigger EOFError. Looking at ext/socket.c, I don't see how that's possible. The socket() call should be returning -1 (with errno set to ENFILE) which should cause init_inetsock_internal to invoke rb_sys_fail("socket(2)"), but clearly that isn't happening. So: ftp.rb gives this error message when there are too many connections open in this process. The fault seems to be that TCPSocket.open claims to succeed when it didn't really. It is a mystery why that happens. -- s=%q( Daniel Martin -- martin@snowplow.org puts "s=%q(#{s})",s.map{|i|i}[1] ) puts "s=%q(#{s})",s.map{|i|i}[1]