IO # читать блоки на неблокирующем сокете?

Руби 1.8.7. Я вызываю чтение для сокета, который был открыт и связан с:

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

Соединение подтверждается вызовом select() и последующим подключением во второй раз в поисках Errno::EISCONN.

Затем я снова вызываю select с тайм-аутом 0, и если возврат не равен нулю, я читаю из сокета, сначала подтверждая, что он имеет установленный O_NONBLOCK:

 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"
     [...]

Все это происходит в цикле один раз в минуту. Вывод в первый раз через цикл:

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

Однако второй раз через цикл зависает здесь:

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

Присоединение gdb к процессу показывает следующую трассировку:

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

Замена вызова read вызовом rcvfrom_nonblock работает, и, что интересно, он не получает EAGAIN, он фактически читает данные (как и следовало ожидать, учитывая возврат от select).

Любые идеи?

Стив


person SteveRawlinson    schedule 19.10.2010    source источник
comment
Я не вижу в вашем коде, где mp устанавливается после select(2). Вы уверены, что он индексирует тот же сокет, который был доступен для чтения?   -  person Nikolai Fetissov    schedule 19.10.2010
comment
Извините, это ошибка, которая закралась, когда я упрощал код вопроса. Как вы можете видеть из вывода (который вырезается и вставляется), это определенно один и тот же сокет.   -  person SteveRawlinson    schedule 20.10.2010
comment
Судя по трассировке gdb, это похоже на гонку в самом интерпретаторе Ruby.   -  person Nikolai Fetissov    schedule 20.10.2010
comment
Я согласен. Я наполовину ожидал ответа типа «Да, IO#read не учитывает флаги, установленные в базовом файловом дескрипторе». Возможно, это предполагаемое поведение, но если это так, оно должно быть задокументировано, потому что это определенно не то, что можно было бы ожидать. Спасибо, что заглянули, Николай.   -  person SteveRawlinson    schedule 21.10.2010
comment
Еще одна вещь - поскольку этот стек показывает потоки, у вас есть несколько потоков? Другие потоки случайно не обращаются к тому же сокету?   -  person Nikolai Fetissov    schedule 21.10.2010
comment
Нет, я вообще не использую нити.   -  person SteveRawlinson    schedule 22.10.2010


Ответы (1)


Вы были правы, ожидая ответа «IO#read не учитывает флаги, установленные в базовом файловом дескрипторе»:

ruby 1.9.3 IO#read

Обратите внимание, что этот метод ведет себя как функция fread() в C. Если вам нужно поведение, подобное системному вызову read(2), рассмотрите readpartial, read_nonblock и sysread.

Я ценю, что вы используете 1.8, но

  • вы видите, что процесс застрял в io_fread, и
  • IO#read_nonblock также доступен в версии 1.8.
person sheldonh    schedule 07.11.2011