From 77deb17e04eb56cdda4ad6c02ed5757d1290e089 Mon Sep 17 00:00:00 2001 From: Mike Mangino Date: Tue, 6 Feb 2018 20:41:16 +0000 Subject: [PATCH] move code from cocaine --- lib/terrapin/command_line/multi_pipe.rb | 42 ++++++++++++++++++++++--- spec/support/nonblocking_examples.rb | 13 ++++++++ 2 files changed, 50 insertions(+), 5 deletions(-) diff --git a/lib/terrapin/command_line/multi_pipe.rb b/lib/terrapin/command_line/multi_pipe.rb index 87c76ef..166de50 100644 --- a/lib/terrapin/command_line/multi_pipe.rb +++ b/lib/terrapin/command_line/multi_pipe.rb @@ -29,19 +29,51 @@ def close_write end def read - @stdout_output = read_stream(@stdout_in) - @stderr_output = read_stream(@stderr_in) + read_streams(@stdout_in, @stderr_in) end def close_read - @stdout_in.close + begin + @stdout_in.close + rescue IOError + # do nothing + end + + begin @stderr_in.close + rescue IOError + # do nothing + end + end + + def read_streams(output, error) + @stdout_output = "" + @stderr_output = "" + read_fds = [output, error] + while !read_fds.empty? + to_read, = IO.select(read_fds) + if to_read.include?(output) + @stdout_output << read_stream(output) + read_fds.delete(output) if output.closed? + end + + if to_read.include?(error) + @stderr_output << read_stream(error) + read_fds.delete(error) if error.closed? + end + end end def read_stream(io) result = "" - while partial_result = io.read(8192) - result << partial_result + begin + while partial_result = io.read_nonblock(8192) + result << partial_result + end + rescue EOFError, Errno::EPIPE + io.close + rescue Errno::EINTR, Errno::EWOULDBLOCK, Errno::EAGAIN + # do nothing end result end diff --git a/spec/support/nonblocking_examples.rb b/spec/support/nonblocking_examples.rb index 73a32e1..b246ae2 100644 --- a/spec/support/nonblocking_examples.rb +++ b/spec/support/nonblocking_examples.rb @@ -1,4 +1,17 @@ shared_examples_for "a command that does not block" do + it "does not block if the command output a lot on stderr" do + cmd = Terrapin::CommandLine.new( + "ruby", + "-e '$stdout.puts %{hello}; $stderr.puts %{goodbye}*10_000'", + :swallow_stderr => false + ) + Timeout.timeout(5) do + cmd.run + end + expect(cmd.command_output).to eq "hello\n" + expect(cmd.command_error_output).to eq "#{"goodbye" * 10_000}\n" + end + it 'does not block if the command outputs a lot of data' do garbage_file = Tempfile.new("garbage") 10.times{ garbage_file.write("A" * 1024 * 1024) }