Here is my understanding about the current state of I/O in Ruby.   
Please correct me where I am mistaken.

- by default, ruby i/o operations block, but only block the calling  
Ruby thread.  Ruby does this by scheduling a thread out if the fd is  
not read-ready/write-ready.  If there is more than one Ruby thread,  
Ruby won't do a read(2) or write(2) on an fd unless select() says it  
is ready, to prevent blocking the entire process.

- the one flaw with this scheme is that write(2) can block even if an  
fd is write-ready, if you try to write too much data.  This will  
cause such a write to lock the entire process and all Ruby threads  
therein ([0] is a simple test program that displays the problem).

- You can try setting O_NONBLOCK on your IO objects with fcntl.  That  
will help you in the case where you only have one Ruby thread -- now  
read and write will raise Errno::EAGAIN if the fd isn't ready.  But  
in the case where there is more than one Ruby thread, this won't work  
because Ruby won't perform the read(2) or write(2) until the fd is  
ready.  So even though you have O_NONBLOCK set, you block your Ruby  
thread.  (See [1] for an example]).

Is this right?  What is the current state of supporting nonblocking i/ 
o in Ruby?

One other question: are the buffered fread()/fwrite() functions  
guaranteed to work correctly if O_NONBLOCK is set on the underlying  
descriptor?  I have not been able to find a good answer to this.

Josh

Example [0]:

thread = Thread.new {
     while true
         puts "Background thread running..."
         sleep 1;
     end
}

# Give the background thread a few chances to show that it's running
sleep 2;

(read_pipe, write_pipe) = IO::pipe

# this will stall the entire process, including the background thread.
# change the length to 4096 and everything is fine.
write_pipe.write(" " * 4097)

thread.join


Example [1]:

require 'fcntl'

thread = Thread.new {
     while true
         puts "Background thread running..."
         sleep 1;
     end
}

(read_pipe, write_pipe) = IO::pipe
read_pipe.fcntl(Fcntl::F_SETFL, read_pipe.fcntl(Fcntl::F_GETFL) |  
Fcntl::O_NONBLOCK)

# this will block our thread, even though the fd is set to nonblocking.
# however, if you eliminate the background thread, this call with  
give you EAGAIN,
# which is what you want.
read_pipe.read

# we will never get here
puts "Finished read!"