Problem description:

Problem occur when you are waiting for two or more
descriptors, one of them have data available in internal
buffer and all descriptors are not ready and there is
another thread.
Function rb_thread_select calls select with zero wait.
select return without any available descriptors (n==0)
thread->readfds and others are not cleared (correct
but not when we have panding buffer).
Then rb_f_select merges result of rb_thread_select
and descriptors with pending buffer.
=> ruby's select call return more read-ready IO objects
than are really available.

This patch solves the problem,
it is againts stable-snapshot
#define RUBY_VERSION "1.6.5"
#define RUBY_RELEASE_DATE "2001-11-23"
#define RUBY_VERSION_CODE 165
#define RUBY_RELEASE_CODE 20011123

-8<-------------------
--- io.c.old    Wed Sep  5 08:53:27 2001
+++ io.c        Sat Nov 24 22:02:11 2001
@@ -2739,8 +2739,12 @@
      }

      max++;
-
-    n = rb_thread_select(max, rp, wp, ep, tp);
+    if (pending) {
+           n = select(max, rp, wp, ep, tp); /* tp is zero wait here */
+    }
+    else {
+           n = rb_thread_select(max, rp, wp, ep, tp);
+    }
      if (n < 0) {
         rb_sys_fail(0);
      }
-8<-------------------

Jakub Travnik
jabber://jtra / jabber.com
ICQ: 66770334 (deprecated)


Jakub Travnik wrote:

> Hello,
> 
> I found a bug in ruby select method.
> Bug is easily repeatable on:
> Linux 2.4.3 with:
> ruby 1.6.5 stable
> ruby stable-snapshot 2001-11-23
> 
> Description:
>  Select call with first paramter filled with IO objects should
> return IO objects that will not block for next read. It does not
> work when there is an another thread. Following code demonstrate
> this:
> --8<---------------
> require 'thread'
> 
> f1=$stdin # choose any devices that you can read interactively
> f2=File.open('/dev/tty8')
> 
> puts "f1: #{f1}"
> puts "f2: #{f2}"
> 
> =begin
> # uncomment this to see bug
> Thread.new{
>   loop{ }
> }
> =end
> 
> loop{
>   puts "select: #{[[f1,f2],nil,nil,nil].inspect}"
>   res=select([f1,f2],nil,nil,nil)
>   puts "select returned: #{res.inspect}"
>   if res
>     res=res[0]
>     if res.include?(f1)
>       puts 'f1 will read'
>       puts 'f1: '+f1.getc.to_s
>     end
>     if res.include?(f2)
>       puts 'f2 will read'
>       puts 'f2: '+f2.getc.to_s
>     end
>   end
> }
> --8<---------------
> 
> I have used free allocated consoles /dev/tty8.
> Make sure you have no X11 running there!
> You can allocate virtual consoles with chvt command.
> However it is not significant for this bug, f1 and f2 can be
> TCPSocket, UNIXSocket too (like in configuration where bug
> appeared first).
> Run this, try that you can type something on f1 and f2
> (on consoles enter is required to flush line).
> 
> You should see, that select is returning one IO object at time
> (as you feed f1 and f2 with text).
> 
> Now, the bug.
> Uncomment thread and run this again. Select returns both f1
> and f2 even when there are no ready data in one of them.
> 
> Thread may be running or blocked in blocking read
> (of another IO object) - it does not matter.
> 
> 
> Jakub Travnik
> jabber://jtra / jabber.com
> ICQ: 6770334 (deprecated)
>