From: Tanaka Akira Date: 2011-08-02T09:24:41+09:00 Subject: [ruby-core:38681] Re: [Ruby 1.9 - Bug #5138][Open] Add nonblocking IO that does not use exceptions for EOF and EWOULDBLOCK 2011/8/2 Urabe Shyouhei : > (08/02/2011 08:14 AM), Eric Wong wrote: >> Urabe Shyouhei 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