Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ruby non-blocking line read

I'm trying to read a line from an io in a non-blocking way.

Unfortunately readline blocks. I think I can solve this with read_nonblock with an additional buffer where I store partial result, check if there are multiple lines in the buffer, etc.. but it seems a bit complicated for a simple task like this. Is there a better way to do this?

Note: I'm using event demultiplexing (select) and I'm quite happy with it, I don't want to create threads, use EventMachine, etc...

like image 231
Karoly Horvath Avatar asked Mar 21 '12 10:03

Karoly Horvath


2 Answers

I think the read_nonblock solution is probably the way to go. Simple, not maximally efficient monkey-patch version:

class IO
  def readline_nonblock
    rlnb_buffer = ""
    while ch = self.read_nonblock(1) 
      rlnb_buffer << ch
      if ch == "\n" then
        result = rlnb_buffer
        return result
      end
    end     
  end
end      

That throws an exception if there's no data ready, just like read_nonblock, so you have to rescue that to just get a nil back, etc.

like image 79
Mark Reed Avatar answered Nov 09 '22 14:11

Mark Reed


This implementation improves on Mark Reed's answer by not discarding data read that does not end in a newline:

class IO
  def readline_nonblock
    buffer = ""
    buffer << read_nonblock(1) while buffer[-1] != "\n"

    buffer
  rescue IO::WaitReadable => blocking
    raise blocking if buffer.empty?

    buffer
  end
end
like image 3
Edward Anderson Avatar answered Nov 09 '22 14:11

Edward Anderson