user1099123
user1099123

Reputation: 6683

Does ruby IO.gets read from a buffer?

Can someone explain to me how pipe.gets works in this instance? is IO object (pipe in this example), buffering the output? How is it that I can take my time to read the output from stdout using "gets"? I even put a sleep in there before reading through gets to make sure I'm not crazy.

def run_script(commands)
      raw_output = nil
      IO.popen("./db", "r+") do |pipe|
        commands.each do |command|
          pipe.puts command
        end

        pipe.close_write

        # Read entire output
        raw_output = pipe.gets(nil)
      end
      raw_output.split("\n")
    end

Upvotes: 2

Views: 476

Answers (1)

3limin4t0r
3limin4t0r

Reputation: 21130

The pipe.sync options defaults to false for popen. Meaning that the commands are buffered by Ruby until you call pipe.close_write, pipe.close, exit the program or the buffer is full. The buffer is then flushed, providing all buffered data written to the other program.

For more info check out the IO#sync documentation and/or What does “file.sync = true” do?

Based on your comment I'm not sure I understand the question. The only reason I can think of why pipe.gets(nil) would return nil is if the other program has no output.

The other option is that pipe.gets(nil) will block. This could happen if pipe is never flushed, meaning the other program is still waiting for the input. Because the other program now blocks and hasn't closed their standard output pipe.gets(nil) will also block. pipe.gets(nil) will read everything from the standard input. How does it know everything is read? It knows because a closed connection means there can't be send any data, thus it waits for a closed connection.

Ruby                stdout -------> stdin    ./db
                    stdin <-------- stdout

                                             (blocked because empty)
pipe.puts("a")      "a\n"  -------> empty    <read from stdin>
                    empty <-------- empty

                                             (still blocked)
pipe.puts("b")      "a\nb\n" -----> empty    <read from stdin>
                    empty <-------- empty

(flushing stdout)                            (continues consuming "a\nb\n")
pipe.close_write    empty ----/---> "a\n\b"  <read from stdin>
                    empty <-------- empty

(blocks because empty)
pipe.gets(nil)      empty ----/---> empty    <send data to stdout>
                    empty <-------- "data\n"

(continues consuming "data\n")               (flushing stdout)
pipe.gets(nil)      empty ----/---> empty    <close stdout>
                    "data\n" <--/-- empty

I hope the above helps demonstrate the process of your current code.

Upvotes: 3

Related Questions