Merge pull request #11344 from Bo98/syscommand-pipe-hang
system_command: avoid waiting on pipes after process termination
This commit is contained in:
commit
05111c878f
@ -178,6 +178,9 @@ class SystemCommand
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
class ProcessTerminatedInterrupt < StandardError; end
|
||||||
|
private_constant :ProcessTerminatedInterrupt
|
||||||
|
|
||||||
sig { params(block: T.proc.params(type: Symbol, line: String).void).void }
|
sig { params(block: T.proc.params(type: Symbol, line: String).void).void }
|
||||||
def each_output_line(&block)
|
def each_output_line(&block)
|
||||||
executable, *args = command
|
executable, *args = command
|
||||||
@ -196,9 +199,23 @@ class SystemCommand
|
|||||||
|
|
||||||
write_input_to(raw_stdin)
|
write_input_to(raw_stdin)
|
||||||
raw_stdin.close_write
|
raw_stdin.close_write
|
||||||
|
|
||||||
|
line_thread = Thread.new do
|
||||||
|
Thread.handle_interrupt(ProcessTerminatedInterrupt => :never) do
|
||||||
each_line_from [raw_stdout, raw_stderr], &block
|
each_line_from [raw_stdout, raw_stderr], &block
|
||||||
|
end
|
||||||
|
rescue ProcessTerminatedInterrupt
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
Thread.pass
|
||||||
|
|
||||||
|
end_time = Time.now + @timeout if @timeout
|
||||||
|
raise Timeout::Error if raw_wait_thr.join(end_time&.remaining).nil?
|
||||||
|
|
||||||
@status = raw_wait_thr.value
|
@status = raw_wait_thr.value
|
||||||
|
|
||||||
|
line_thread.raise ProcessTerminatedInterrupt.new
|
||||||
|
line_thread.join
|
||||||
rescue Interrupt
|
rescue Interrupt
|
||||||
Process.kill("INT", pid) if pid && !sudo?
|
Process.kill("INT", pid) if pid && !sudo?
|
||||||
raise Interrupt
|
raise Interrupt
|
||||||
@ -214,21 +231,29 @@ class SystemCommand
|
|||||||
|
|
||||||
sig { params(sources: T::Array[IO], _block: T.proc.params(type: Symbol, line: String).void).void }
|
sig { params(sources: T::Array[IO], _block: T.proc.params(type: Symbol, line: String).void).void }
|
||||||
def each_line_from(sources, &_block)
|
def each_line_from(sources, &_block)
|
||||||
end_time = Time.now + @timeout if @timeout
|
|
||||||
|
|
||||||
sources = {
|
sources = {
|
||||||
sources[0] => :stdout,
|
sources[0] => :stdout,
|
||||||
sources[1] => :stderr,
|
sources[1] => :stderr,
|
||||||
}
|
}
|
||||||
|
|
||||||
loop do
|
pending_interrupt = T.let(false, T::Boolean)
|
||||||
readable_sources, = IO.select(sources.keys, [], [], end_time&.remaining!)
|
|
||||||
raise Timeout::Error if readable_sources.nil?
|
until pending_interrupt
|
||||||
|
readable_sources = T.let([], T::Array[IO])
|
||||||
|
begin
|
||||||
|
Thread.handle_interrupt(ProcessTerminatedInterrupt => :on_blocking) do
|
||||||
|
readable_sources = T.must(IO.select(sources.keys)).fetch(0)
|
||||||
|
end
|
||||||
|
rescue ProcessTerminatedInterrupt
|
||||||
|
readable_sources = sources.keys
|
||||||
|
pending_interrupt = true
|
||||||
|
end
|
||||||
|
|
||||||
break if readable_sources.none? do |source|
|
break if readable_sources.none? do |source|
|
||||||
|
loop do
|
||||||
line = source.readline_nonblock || ""
|
line = source.readline_nonblock || ""
|
||||||
yield(sources.fetch(source), line)
|
yield(sources.fetch(source), line)
|
||||||
true
|
end
|
||||||
rescue EOFError
|
rescue EOFError
|
||||||
source.close_read
|
source.close_read
|
||||||
sources.delete(source)
|
sources.delete(source)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user