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

  • Thread starter Just Another Victim of the Ambient Morality
  • Start date
J

Just Another Victim of the Ambient Morality

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
 
L

Luis Lavena

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"

===

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 :'(

Regards,
 
H

Heesob Park

Hi,

Luis said:
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 :'(

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, 0)) {

if (SAFE_FD_ISSET(handle_slot_to_fd, rd)) {
rb_w32_fdset(handle_slot_to_fd, &aread);
}
if (SAFE_FD_ISSET(handle_slot_to_fd, wr)) {
rb_w32_fdset(handle_slot_to_fd, &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
 
J

James Tucker


Yup, complete hard block on read()

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 =)

Right, the blocking call is probably still there, however I've made no
attempt to verify.
Here is the patched code of rb_win_selected function

Are there any known problems with this patch?
 
L

Luis Lavena

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.

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 :)

Regards and thank you again,
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,969
Messages
2,570,161
Members
46,705
Latest member
Stefkari24

Latest Threads

Top