rubysocketsnonblocking

IO#read blocks on non-blocking socket?


Ruby 1.8.7. I'm calling read on a socket which has been opened and connected with:

socket = Socket.new(AF_INET, SOCK_STREAM, 0)
sockaddr = Socket.sockaddr_in(mp.port, mp.ip_address.ip)
begin
  socket.connect_nonblock(sockaddr)
[...]

The connection is confirmed by calling select() and then connecting a second time looking for Errno::EISCONN.

I then call select again with a 0 timeout and if the return is not nil I read from the socket, first confirming that it has O_NONBLOCK set:

 rc = select([socket], nil, nil, 0)
 puts "  select returned: #{rc.pretty_inspect}"
 if rc
   begin
     puts "  reading: #{socket} nonblock: #{socket.fcntl(Fcntl::F_GETFL) & Fcntl::O_NONBLOCK}"
     response = socket.read
     puts "  done reading"
     [...]

This all happens in a loop once per minute. The output the first time through the loop is:

select returned: [[#<Socket:0xb6e0dcb8>], [], []]
reading: #<Socket:0xb6e0dcb8> nonblock: 2048
done reading

However the second time through the loop hangs here:

select returned: [[#<Socket:0xb6e0dcb8>], [], []]
reading: #<Socket:0xb6e0dcb8> nonblock: 2048

Attaching gdb to the process shows this backtrace:

0 0xffffe410 in __kernel_vsyscall ()
1 0xb7e5539d in select () from /lib/tls/i686/cmov/libc.so.6
2 0x08064368 in rb_thread_schedule () at eval.c:11020
3 0x080785bb in io_fread (

Replacing the call to read with a call to rcvfrom_nonblock works, and interestingly it does not get EAGAIN, it actually reads data (as you would expect given the return from select).


Solution

  • You were right to expect the answer "IO#read does not respect the flags set on the underlying file descriptor":

    ruby 1.9.3 IO#read

    Note that this method behaves like fread() function in C. If you need the behavior like read(2) system call, consider readpartial, read_nonblock and sysread.

    I appreciate that you're using 1.8, but

    • you're seeing the process stuck in io_fread, and
    • IO#read_nonblock is available in 1.8 as well.