Here's one possible solution.  Does anyone know how to get rid of this
ugly eval hack?

Paul

---

setvbuf.c:
  #include <ruby.h>
  #include <rubyio.h>

  static VALUE ruby_setvbuf(VALUE self, VALUE mode, VALUE buf)
  {
    char * c_buf = (buf == Qnil) ? NULL : STR2CSTR(buf);
    size_t size = (buf == Qnil) ? 0 : RSTRING(buf)->len;
    int c_mode = NUM2INT(mode);
    OpenFile * fptr;
    FILE * f1, * f2;

    GetOpenFile(self, fptr);
    f1 = GetReadFile(fptr);
    f2 = GetWriteFile(fptr);

    setvbuf(f1, c_buf, c_mode, size);
    if(f1 != f2)
    {
      setvbuf(f2, c_buf, c_mode, size);
    }

    return Qnil;
  }

  void Init_setvbuf()
  {
    rb_define_method(rb_cIO, "c_setvbuf", ruby_setvbuf, 2);
    rb_eval_string(
        "class IO\n"
        "  PIPE_BUFFERS = Hash.new\n"
        "  def self.pipe_buffer_finalizer(buf)\n"
        "    PIPE_BUFFERS[buf] = true\n"
        "    return proc { PIPE_BUFFERS.delete(buf) }\n"
        "  end\n"
        "  def setvbuf(mode, buf=nil)\n"
        "    if buf then\n"
        "      ObjectSpace.define_finalizer(self, IO.pipe_buffer_finalizer(buf))\n"
        "    end\n"
        "    c_setvbuf(mode, buf)\n"
        "  end\n"
        "end\n");
    rb_define_const(rb_cIO, "NBF", INT2NUM(_IONBF));
    rb_define_const(rb_cIO, "LBF", INT2NUM(_IOLBF));
    rb_define_const(rb_cIO, "FBF", INT2NUM(_IOFBF));

pipe.rb:
  require 'open3'

  Open3.popen3('ruby test.rb') do |pin, pout, perr|
    write_map = { pout => $stdout, perr => $stderr, $stdin => pin }
    loop do
      retval = select([pout, perr, $stdin], nil, [pin, $stdout, $stderr], nil)
      puts "select returned"
      for i in retval[0]
        str = i.read(512)
        write_map[i].write(str)
      end
      for i in retval[2]
        exit(1)
      end
    end
  end

test.rb:
  require 'setvbuf'

  $stdout.setvbuf(IO::NBF)
  $stdout.sync = false

  loop do
    puts "This is a test."
    sleep 0.01
  end