Is this a threading bug in Ruby-185-21 on Win32?

I'm trying to write a program that will poll some website and that is
easily terminable. I have a similar Ruby program on a Linux machine that
polls my inbox to see if I have mail and, whenever I have new mail, prints
the \b character. This is done by putting the polling code in a loop in a
thread block while the main thread simply waits in a gets method. This
allows someone to exit the program simply by typing anything. However, when
I try this on Win32, it appears that the Ruby interpreter blocks in the gets
call and the other thread never gets any execution time.
    So, there appears to be a great discrepancy between the implementations
on the two systems. Is this really the case? Why is this? Is this
necessarily so or can we change this? Shouldn't we like to change this?
What do you all think?
    What follows is some example code. On my Win32 system, this program
only counts up to 102.
    Thank you for looking over this...

puts 'start'
    Thread.new do
    count = 0
    loop do
        puts count
        count += 1
    end
end
gets

I've asked about this back in September 2007 at ruby-core mailing
list, no answer. I'm copying my post with details more information:

Hello Ruby Developers.

I've been trying to determine what is wrong with this simple script:

http://pastie.caboo.se/101434

t = Thread.new {
   while true
     puts "printing a line"
     sleep 2
   end
}

gets
t.exit
puts "exiting"

···

On Mar 27, 2:41 am, "Just Another Victim of the Ambient Morality" <ihates...@hotmail.com> wrote:

    I'm trying to write a program that will poll some website and that is
easily terminable. I have a similar Ruby program on a Linux machine that
polls my inbox to see if I have mail and, whenever I have new mail, prints
the \b character. This is done by putting the polling code in a loop in a
thread block while the main thread simply waits in a gets method. This
allows someone to exit the program simply by typing anything. However, when
I try this on Win32, it appears that the Ruby interpreter blocks in the gets
call and the other thread never gets any execution time.
    So, there appears to be a great discrepancy between the implementations
on the two systems. Is this really the case? Why is this? Is this
necessarily so or can we change this? Shouldn't we like to change this?
What do you all think?
    What follows is some example code. On my Win32 system, this program
only counts up to 102.
    Thank you for looking over this...

puts 'start'
    Thread.new do
    count = 0
    loop do
        puts count
        count += 1
    end
end
gets

===

And found that all the IO (stdin) is broken on Windows:
custom build with MinGW (3.4.5 -- mingw special)
custom build with VC8
official build VC6

All behave the same way: just 1 line of "printing a line" gets
actually printed, and the world halt until you hit enter.

I saw a few post dating 2003 about this... and this brakes the
cross-platform nature of most of ruby: works on some platform, don't
work on other.

Since 1.8.6 will stay with us a bit longer, any ideas how to solve it?

Park Heesob posted a patch back then, but it don't work as expected
(this problem didn't get solved).

For the record: 1.9.0 don't show this issue, but YARV is another
different breed.

Thanks in advance for your time.

==

Even it doesn't solve your issue, explain it better.

Still, no answer, no solution :cry:

Regards,
--
Luis Lavena

Hi,

Luis Lavena wrote:

necessarily so or can we change this? Shouldn't we like to change this?
        count += 1
    end
end
gets

I've asked about this back in September 2007 at ruby-core mailing
list, no answer. I'm copying my post with details more information:

Hello Ruby Developers.

I've been trying to determine what is wrong with this simple script:

http://pastie.caboo.se/101434

t = Thread.new {
   while true
     puts "printing a line"
     sleep 2
   end
}

gets
t.exit
puts "exiting"

===

And found that all the IO (stdin) is broken on Windows:
custom build with MinGW (3.4.5 -- mingw special)
custom build with VC8
official build VC6

All behave the same way: just 1 line of "printing a line" gets
actually printed, and the world halt until you hit enter.

I saw a few post dating 2003 about this... and this brakes the
cross-platform nature of most of ruby: works on some platform, don't
work on other.

Since 1.8.6 will stay with us a bit longer, any ideas how to solve it?

Park Heesob posted a patch back then, but it don't work as expected
(this problem didn't get solved).

For the record: 1.9.0 don't show this issue, but YARV is another
different breed.

Thanks in advance for your time.

==

Even it doesn't solve your issue, explain it better.

Still, no answer, no solution :cry:

Regards,

Here is the patched code of rb_win_selected function
(win32.c,ruby-1.8.6-p111)
It will fix Thread blocking problem with standard input waiting.

long
rb_w32_select (int nfds, fd_set *rd, fd_set *wr, fd_set *ex,
         struct timeval *timeout)
{
    DWORD ms_total, limit;
    HANDLE handles[MAXIMUM_WAIT_OBJECTS];
    int handle_slot_to_fd[MAXIMUM_WAIT_OBJECTS];
    int n_handles = 0, i;
    fd_set aread, awrite, aexcept;
    int retcode;
    long r;
    fd_set file_rd;
    fd_set file_wr;
#ifdef USE_INTERRUPT_WINSOCK
    fd_set trap;
#endif /* USE_INTERRUPT_WINSOCK */
    int file_nfds;

#define SAFE_FD_ISSET(fd, set) (set != NULL && rb_w32_fdisset(fd, set))
/* calculate how long we need to wait in milliseconds */
if (timeout == NULL) {
  ms_total = INFINITE;
} else {
  ms_total = timeout->tv_sec * 1000;
  ms_total += timeout->tv_usec / 1000;
}

/* build an array of handles for non-sockets */
for (i = 0; i <= nfds; i++) {
  if (SAFE_FD_ISSET(i, rd) || SAFE_FD_ISSET(i, wr)) {
   handles[n_handles] = (HANDLE)TO_SOCKET(i);
   if ((SOCKET)handles[n_handles]==TO_SOCKET(0)) { /* only treat stdin
*/
    handle_slot_to_fd[n_handles] = i;
    n_handles++;
   }
  }
}

    if (!NtSocketsInitialized) {
  StartSockets();
    }
    r = 0;
    if (rd && rd->fd_count > r) r = rd->fd_count;
    if (wr && wr->fd_count > r) r = wr->fd_count;
    if (ex && ex->fd_count > r) r = ex->fd_count;
    if (nfds > r) nfds = r;
    if (nfds == 0 && timeout) {
  Sleep(timeout->tv_sec * 1000 + timeout->tv_usec / 1000);
  return 0;
    }
    file_nfds = extract_file_fd(rd, &file_rd);
    file_nfds += extract_file_fd(wr, &file_wr);
    if (file_nfds)
    {
        if(n_handles>0) {
    FD_ZERO(&aread);
    FD_ZERO(&awrite);

          limit = GetTickCount() + ms_total;

          do {
    DWORD wret;
    retcode = 0;

       wret = MsgWaitForMultipleObjects(n_handles, handles, FALSE,
           retcode > 0 ? 0 : 100, QS_ALLEVENTS);

       if (wret == WAIT_TIMEOUT) {
       /* set retcode to 0; this is the default.
        * select() may have set it to something else,
        * in which case we leave it alone, so this branch
        * does nothing */
       ;
       } else if (wret == WAIT_FAILED) {
    if (retcode == 0) {
         retcode = -1;
        }
       } else {
      if (retcode < 0) {
       retcode = 0;
       }
       for (i = 0; i < n_handles; i++) {
        if (WAIT_OBJECT_0 == WaitForSingleObject(handles[i], 0)) {

        if (SAFE_FD_ISSET(handle_slot_to_fd[i], rd)) {
         rb_w32_fdset(handle_slot_to_fd[i], &aread);
        }
        if (SAFE_FD_ISSET(handle_slot_to_fd[i], wr)) {
         rb_w32_fdset(handle_slot_to_fd[i], &awrite);
        }

        retcode++;
        }
    }
     }
     } while (retcode == 0 &&
            (ms_total == INFINITE || GetTickCount() < limit));
     if (rd) *rd = aread;
     if (wr) *wr = awrite;
        }
        else {
    // assume normal files are always readable/writable
    // fake read/write fd_set and return value
    if (rd) *rd = file_rd;
    if (wr) *wr = file_wr;
  }
  return file_nfds;
    }

#if USE_INTERRUPT_WINSOCK
    if (ex)
  trap = *ex;
    else
  trap.fd_count = 0;
    if (trap.fd_count < FD_SETSIZE)
  trap.fd_array[trap.fd_count++] = (SOCKET)interrupted_event;
    // else unable to catch interrupt.
    ex = &trap;
#endif /* USE_INTERRUPT_WINSOCK */

    RUBY_CRITICAL({
  r = select(nfds, rd, wr, ex, timeout);
  if (r == SOCKET_ERROR) {
      errno = map_errno(WSAGetLastError());
  }
    });
    return r;
}

Regards,
Park Heesob

···

On Mar 27, 2:41 am, "Just Another Victim of the .. Morality" > <ihates...@hotmail.com> wrote:

--
Posted via http://www.ruby-forum.com/\.

Hi,

Luis Lavena wrote:

necessarily so or can we change this? Shouldn't we like to change this?
       count += 1
   end
end
gets

I've asked about this back in September 2007 at ruby-core mailing
list, no answer. I'm copying my post with details more information:

Hello Ruby Developers.

I've been trying to determine what is wrong with this simple script:

http://pastie.caboo.se/101434

t = Thread.new {
  while true
    puts "printing a line"
    sleep 2
  end
}

gets
t.exit
puts "exiting"

===

And found that all the IO (stdin) is broken on Windows:
custom build with MinGW (3.4.5 -- mingw special)
custom build with VC8
official build VC6

All behave the same way: just 1 line of "printing a line" gets
actually printed, and the world halt until you hit enter.

Yup, complete hard block on read()

I saw a few post dating 2003 about this... and this brakes the
cross-platform nature of most of ruby: works on some platform, don't
work on other.

Since 1.8.6 will stay with us a bit longer, any ideas how to solve it?

HighLine and EventMachine have both proven possible for me. I hacked together a mini-irb tool which is then more useful for stab testing something like GServer in irb. Mind you, after loading up eventmachines reactor for the keyboard, using GServer seems a little silly =)

Park Heesob posted a patch back then, but it don't work as expected
(this problem didn't get solved).

For the record: 1.9.0 don't show this issue, but YARV is another
different breed.

Right, the blocking call is probably still there, however I've made no attempt to verify.

Thanks in advance for your time.

==

Even it doesn't solve your issue, explain it better.

Still, no answer, no solution :cry:

Regards,

Here is the patched code of rb_win_selected function

Are there any known problems with this patch?

···

On 27 Mar 2008, at 09:38, Heesob Park wrote:

On Mar 27, 2:41 am, "Just Another Victim of the .. Morality" >> <ihates...@hotmail.com> wrote:

Thank you Park, I'll test this later also with MinGW build

Did you know why still didn't get merged?

I was about to scream about it at ruby-core, but after the post on
rubyinside, I don't want to annoy more people :slight_smile:

Regards and thank you again,

···

On Mar 27, 6:38 am, Heesob Park <pha...@gmail.com> wrote:

Here is the patched code of rb_win_selected function
(win32.c,ruby-1.8.6-p111)
It will fix Thread blocking problem with standard input waiting.

--
Luis Lavena