Issue #5539 has been updated by Christopher Huff.


The issue pops up when Ruby uses the Editline library instead of GNU Readline. I suspect it is actually some conditionally compiled code that depends on the library being used, not the readline() call itself, since both the above workaround and GNU Readline work as expected.
----------------------------------------
Bug #5539: Readline.readline() blocks all threads
http://redmine.ruby-lang.org/issues/5539

Author: Christopher Huff
Status: Open
Priority: Normal
Assignee: 
Category: 
Target version: 
ruby -v: ruby 1.9.3p0 (2011-10-30 revision 33570) [x86_64-darwin10.8.0]


The standard library Readline.readline() function blocks the thread it's called from without unlocking the VM, thus blocking all threads. This code demonstrates the problem:

require 'pp'
t = Thread.new {10.times {|x| puts x; sleep 1}}
while(buf = Readline.readline)
    p buf
    if(buf == 'q')
        break
    end
end

Thread t will be blocked by the readline call, numbers being printed occasionally when lines are entered and readline() returns, rather than once per second. Reproduced on ruby 1.9.2p180 (2011-02-18 revision 30909) [x86_64-darwin10.6.0] and ruby 1.9.3p0 (2011-10-30 revision 33570) [x86_64-darwin10.8.0].

The RubyInline gem allows a workaround:

require 'inline'
class BetterReadline
    inline :C do |builder|
        builder.include '<ruby.h>'
        builder.include '<readline/readline.h>'
src = <<END
    VALUE readline_intern(void * data) {
        char ** rstr = (char **)data;
        *rstr = readline(NULL);
        return Qnil;
    }
END
        builder.prefix(src)
src = <<END
    VALUE rb_readline(void) {
        char * str = NULL;
        rb_thread_blocking_region(readline_intern, &str, NULL, NULL);
        return rb_str_new2(str);
    }
END
        builder.c_singleton(src, method_name: 'readline')
    end
end # class BetterReadline


This then illustrates the desired behavior, with the thread running in the background while readline() waits for input:

require 'pp'
t = Thread.new {10.times {|x| puts x; sleep 1}}
while(buf = BetterReadline.readline)
    p buf
    if(buf == 'q')
        break
    end
end


-- 
http://redmine.ruby-lang.org