tompave
tompave

Reputation: 12412

Ruby STDIN, blocking vs not blocking

I'm trying to find some documentation on how STDIN is handled in Ruby.

I've experimented with this simple script:

# test.rb

loop do
  puts "stdin: #{$stdin.gets}"
  sleep 2
end

That I've run from bash (on OS X) with:

$ ruby test.rb

As I expected, the call to $stdin.gets is blocking, and the loop waits for the next input. The 2 second sleep time even allows me to enter more lines in one go, and the loop correctly prints them in order, then stops again when STDIN is cleared:

$ ruby test.rb
a
stdin: a
b
stdin: b
c
d
e
stdin: c
stdin: d
stdin: e

So far, all good. I was expecting this.

Then, I made a test with a pipe:

$ mkfifo my_pipe
$ ruby test.rb < my_pipe

And, in another shell:

$ echo "Hello" > my_pipe

This time, it behaved a bit differently.
At first it did wait, blocking the loop. But then, after the first input was passed through the pipe, it keept looping and printing empty strings:

$ ruby test.rb
stdin: Hello
stdin:
stdin:
stdin: Other input
stdin:

So my question is: why the difference? Does it treat the pipe like an empty file? Where is this documented? The docs don't say anything about the blocking behaviour, but they do say that:

Returns nil if called at end of file.

It's a start.

Upvotes: 0

Views: 545

Answers (1)

Tim Tom
Tim Tom

Reputation: 809

So the short answer is yes, you are getting an EOF from the pipe. Because the way echo works is that it's going to open the pipe for writing, write to it, then close (i.e. send EOF). Then a new call to echo will open it back up, read to it, and then close.

If you had instead used a program that printed lines of a file after a 3 second sleep, you would see that your application would perform blocking waits until that one exits (at which point the never-ending EOFs would return).

# slow_write.rb
ARGF.each do |line|
  puts line
  STDOUT.flush
  sleep 3
end

I should note that this behavior is not specific to Ruby. The C stdlio library has the exact same behavior and since most languages use C primitives as their basis they have the same behavior as well.

Upvotes: 1

Related Questions