B
Brent Roman
The Readline uses the the GNU readline library's event handling hook
to try to ensure that other ruby threads do not block while it is
awaiting user keystrokes from STDIN. I assert that the use of the event
hook for this purpose is unnecessary and, furthermore, the current
implementation can set the Thread.critical flag in other threads.
The current threadling_event() hook function is:
static int
readline_event()
{
CHECK_INTS;
rb_thread_schedule(); //bug here if Thread.critical !!!
return 0;
}
If Thread.critical is set when readline is invoked, readline_event()
will still schedule any other thread that becomes ready. This can
cause another thread, that was not in a critical section, to interrupt
one that was. So, now we have a thread becoming "critical"
asynchronously. This leads to some very weird lockups -- some of which
have been reported on this mailing list.
I think a quick fix might be to remove the unconditional
rb_thread_schedule() call from readline_event. But, a better
fix is to remove the readline_event() function entirely.
So, how will we avoid blocking other threads during keyboard reads?
Replace readline_event() with this:
static int
readline_getc (ignored)
FILE *ignored;
{
VALUE string = rb_funcall (rb_stdin, rb_intern("sysread"),
1, INT2FIX(1));
return RSTRING(string)->ptr[0]; //single byte read
}
We redefine the function that GNU readline uses to read the input stream
to be one based on Ruby's IO#sysread and let Ruby's own IO class
deal with all its threading nastyness.
This has been tested (fairly extensively) in our own multi-threading
motion control application under Ruby 1.6.8. The same bug appears
to exist in the 1.8.x series, although I have not tried
this patch against 1.8.x. I'm hoping someone on the list whose
running 1.8.x (everyone?) will test this.
The changes to ext/readline/readline.c are confined to the first few
lines. Everything from the readline_readline() fn on remains unchanged:
/* readline.c -- GNU Readline module
Copyright (C) 1997-1998 Shugo Maeda */
#include <errno.h>
#include <stdio.h>
#include <readline/readline.h>
#include <readline/history.h>
#include "ruby.h"
#include "rubysig.h"
#include "rubyio.h"
static VALUE mReadline;
#define TOLOWER(c) (isupper(c) ? tolower(c) : c)
#define COMPLETION_PROC "completion_proc"
#define COMPLETION_CASE_FOLD "completion_case_fold"
#ifndef READLINE_42_OR_LATER
# define rl_filename_completion_function filename_completion_function
# define rl_username_completion_function username_completion_function
# define rl_completion_matches completion_matches
#endif
static int
readline_getc (ignored)
FILE *ignored;
{
VALUE string = rb_funcall (rb_stdin, rb_intern("sysread"), 1,
INT2FIX(1));
return RSTRING(string)->ptr[0]; //single byte read
}
to try to ensure that other ruby threads do not block while it is
awaiting user keystrokes from STDIN. I assert that the use of the event
hook for this purpose is unnecessary and, furthermore, the current
implementation can set the Thread.critical flag in other threads.
The current threadling_event() hook function is:
static int
readline_event()
{
CHECK_INTS;
rb_thread_schedule(); //bug here if Thread.critical !!!
return 0;
}
If Thread.critical is set when readline is invoked, readline_event()
will still schedule any other thread that becomes ready. This can
cause another thread, that was not in a critical section, to interrupt
one that was. So, now we have a thread becoming "critical"
asynchronously. This leads to some very weird lockups -- some of which
have been reported on this mailing list.
I think a quick fix might be to remove the unconditional
rb_thread_schedule() call from readline_event. But, a better
fix is to remove the readline_event() function entirely.
So, how will we avoid blocking other threads during keyboard reads?
Replace readline_event() with this:
static int
readline_getc (ignored)
FILE *ignored;
{
VALUE string = rb_funcall (rb_stdin, rb_intern("sysread"),
1, INT2FIX(1));
return RSTRING(string)->ptr[0]; //single byte read
}
We redefine the function that GNU readline uses to read the input stream
to be one based on Ruby's IO#sysread and let Ruby's own IO class
deal with all its threading nastyness.
This has been tested (fairly extensively) in our own multi-threading
motion control application under Ruby 1.6.8. The same bug appears
to exist in the 1.8.x series, although I have not tried
this patch against 1.8.x. I'm hoping someone on the list whose
running 1.8.x (everyone?) will test this.
The changes to ext/readline/readline.c are confined to the first few
lines. Everything from the readline_readline() fn on remains unchanged:
/* readline.c -- GNU Readline module
Copyright (C) 1997-1998 Shugo Maeda */
#include <errno.h>
#include <stdio.h>
#include <readline/readline.h>
#include <readline/history.h>
#include "ruby.h"
#include "rubysig.h"
#include "rubyio.h"
static VALUE mReadline;
#define TOLOWER(c) (isupper(c) ? tolower(c) : c)
#define COMPLETION_PROC "completion_proc"
#define COMPLETION_CASE_FOLD "completion_case_fold"
#ifndef READLINE_42_OR_LATER
# define rl_filename_completion_function filename_completion_function
# define rl_username_completion_function username_completion_function
# define rl_completion_matches completion_matches
#endif
static int
readline_getc (ignored)
FILE *ignored;
{
VALUE string = rb_funcall (rb_stdin, rb_intern("sysread"), 1,
INT2FIX(1));
return RSTRING(string)->ptr[0]; //single byte read
}