2011/8/2 Urabe Shyouhei <shyouhei / ruby-lang.org>:
> (08/02/2011 08:14 AM), Eric Wong wrote:
>> Urabe Shyouhei <shyouhei / ruby-lang.org> wrote:
>>> So when you  do a read loop,  nothing bothers you, as long  as you use
>>> readpartial.
>>
>> That use of select + readpartial is unsafe.
>
> Unsafe how?  readpatial works even without no data on a buffer.

I know it is not safe for SSL and gzipped stream.

IO.select works on raw sockets, i.e. encrypted/compressed stream.
The readability of the raw socket doesn't mean readability of
the decrypted/uncompressed stream.

So following may block the loop at IO.select or readpartial.

  read_streams = [ stream1, stream2, ... ]
  loop do
     # If a stream have buffered data, it may block.
     # (The buffer of IO class is handled by IO.select but
     # other buffers are ignored.)
     r, w, e = IO.select(read_streams)
     r.each do |f|
       # if stream doesn't send enough chunk, it may block.
       str = f.readpartial
     end
  end

read_nonblock can avoid the blocking of the readpartial but
IO.select can still blocks.

  # not tested.
  read_streams = [ stream1, stream2, ... ]
  loop do
     # If a stream have buffered data, it may block.
     # (The buffer of IO class is handled by IO.select but
     # other buffers are ignored.)
     r, w, e = IO.select(read_streams)
     r.each do |f|
       begin
         str = f.read_nonblock
       rescue IO::WaitReadable, IO::WaitWritable
         # xxx: busy loop.
       end
     end
  end

So the right way to nonblocking polymorphic read is call read_nonblock
first and call IO.select only when IO::WaitReadable or IO::WaitWritable is
raised.
(We can assume the buffer is empty if read_nonblock raise IO::WaitReadable
or IO::WaitWritable.)

  # not tested.
  read_streams = [ stream1, stream2, ... ]
  readable = read_streams.dup
  wait_readable = []
  wait_writable = []
  loop do
    readable.each {|f|
      begin
        str = f.read_nonblock
      rescue IO::WaitReadable
        readable.delete f
	wait_readable << f
      rescue IO::WaitWritable
	# OpenSSL::Buffering#read_nonblock may raise IO::WaitWritable.
        readable.delete f
	wait_writable << f
      end
    }
    if readable.empty?
      # call IO.select with zero timeout if readable is not empty?
      rs, ws = IO.select(wait_readable, wait_writable)
      if rs
	wait_readable -= rs
	readable.concat rs
      end
      if ws
	wait_writable -= ws
	readable.concat ws
      end
    end
  end
-- 
Tanaka Akira