IO.popen hangs reading an empty pipe?


#1

How does one check a pipe (created using IO.popen) to see if there is
input waiting without using IO#eof or reading from the pipe? Doing
either of these seems to hang forever if the pipe is empty.

Here’s an example:

require ‘timeout’
IO.popen(“cat”, “r+”) do |pipe|

pipe.puts “hello world” # Uncomment this to prevent hang below.

Timeout::timeout(2) do
if not pipe.eof? # This will hang if the pipe is empty.
p pipe.getc
end
end
end

-Payton


#2

On Tue, 31 Jan 2006, Payton S. wrote:

How does one check a pipe (created using IO.popen) to see if there is input
waiting without using IO#eof or reading from the pipe? Doing either of these
seems to hang forever if the pipe is empty.

 harp:~ > cat a.rb
 IO.popen("cat", "r+") do |pipe|
   pipe.puts 42
   pipe.close_write
   puts pipe.read
 end


 harp:~ > ruby a.rb
 42

consider that cat reads stdin until eof is found.

Here’s an example:

require ‘timeout’
IO.popen(“cat”, “r+”) do |pipe|

pipe.puts “hello world” # Uncomment this to prevent hang below.

not really - only the first one. continue to read an you’ll still hang.
this
is because cat is still waiting on more stdin or eof. so you must send
more or
close the write end of the pipe to send eof.

Timeout::timeout(2) do

never mix timeout with reads. it fails on windoze - if you care.

if not pipe.eof? # This will hang if the pipe is empty.

because cat is waiting for stdin and is not finsihed - cat will only
close it’s
stdout when it’s done and it’s only done when it’s stdin has all been
read.
man 1 cat.

 p pipe.getc

end
end
end

if you are on *nix you’ll find my session module much easier to use:

 harp:~ > gem install session
 Attempting local installation of 'session'
 Local gem file not found: session*.gem
 Attempting remote installation of 'session'
 Updating Gem source index for: http://gems.rubyforge.org
 Successfully installed session-2.4.0
 harp:~ > cat a.rb
 require "rubygems"
 require "session"
 require "stringio"


 sh = Session::new

 stdin = "42"
 stdout = StringIO::new
 stderr = StringIO::new

 sh.execute "cat", "stdin" => stdin, "stdout" => stdout, "stderr" => 

stderr

 puts stdout


 harp:~ > ruby a.rb
 42

session can also be made to be thread-safe (eg use with timeout) if
needed. see docs for more.

kind regards.

-a


#3

Ah. You’re right, of course. I could have sworn that I tried using
pipe.close_write before and the problem remained, but apparently I was
mistaken.

Notes:

  • “cat” was just to prove a point - the real issue was with
    “sendmail”, but it seems to have the same solution. My brief overview
    made me think session was not suited for sendmail, but perhaps I was
    wrong about that too? I’ll check it out.
  • the use of timeout was just to allow the test to exit when the hang
    occurred. I didn’t realize it fails on w32. Is it Timeout that fails,
    or just when timeout is combined with IO reads?

Thanks again!
-Payton


#4

On Tue, 31 Jan 2006, Payton S. wrote:

Ah. You’re right, of course. I could have sworn that I tried using
pipe.close_write before and the problem remained, but apparently I was
mistaken.

Notes:

  • “cat” was just to prove a point - the real issue was with “sendmail”, but
    it seems to have the same solution. My brief overview made me think session
    was not suited for sendmail, but perhaps I was wrong about that too? I’ll
    check it out.

you can do anything with session that bash can do - not sure if it’ll
work for
sendmail or not. should work anywhere you are dealing with
stdin/out/err.

  • the use of timeout was just to allow the test to exit when the hang
    occurred. I didn’t realize it fails on w32. Is it Timeout that fails, or
    just when timeout is combined with IO reads?

i may have mispoke - you may be able to use a thread and timeout - but
it very
easy to block an entire process with io and threads in win… fyi.

cheers.

-a


#5

removed_email_address@domain.invalid wrote:

  • the use of timeout was just to allow the test to exit when the hang
    occurred. I didn’t realize it fails on w32. Is it Timeout that
    fails, or just when timeout is combined with IO reads?

i may have mispoke - you may be able to use a thread and timeout - but
it very
easy to block an entire process with io and threads in win… fyi.

I’ve mixed threads, timeout, and sockets on windows with no problems.
But I haven’t done this with pipes…

ISTR that the problem on windows is with terminal io.