lib/mini_magick/shell.rb in mini_magick-4.6.1 vs lib/mini_magick/shell.rb in mini_magick-4.7.0

- old
+ new

@@ -11,28 +11,23 @@ class Shell def run(command, options = {}) stdout, stderr, status = execute(command, stdin: options[:stdin]) - case status - when 1 + if status != 0 && options.fetch(:whiny, MiniMagick.whiny) fail MiniMagick::Error, "`#{command.join(" ")}` failed with error:\n#{stderr}" - when 127 - fail MiniMagick::Error, stderr - end if options.fetch(:whiny, MiniMagick.whiny) + end $stderr.print(stderr) unless options[:stderr] == false [stdout, stderr, status] end def execute(command, options = {}) stdout, stderr, status = log(command.join(" ")) do - Timeout.timeout(MiniMagick.timeout) do - send("execute_#{MiniMagick.shell_api.gsub("-", "_")}", command, options) - end + send("execute_#{MiniMagick.shell_api.gsub("-", "_")}", command, options) end [stdout, stderr, status.exitstatus] rescue Errno::ENOENT, IOError ["", "executable not found: \"#{command.first}\"", 127] @@ -41,23 +36,41 @@ private def execute_open3(command, options = {}) require "open3" - Open3.capture3(*command, binmode: true, stdin_data: options[:stdin].to_s) + in_w, out_r, err_r, subprocess_thread = Open3.popen3(*command) + + capture_command(in_w, out_r, err_r, subprocess_thread, options) end def execute_posix_spawn(command, options = {}) require "posix-spawn" - pid, stdin, stdout, stderr = POSIX::Spawn.popen4(*command) - [stdin, stdout, stderr].each(&:binmode) - stdin.write(options[:stdin].to_s) - out = stdout.read - err = stderr.read - Process.waitpid(pid) + pid, in_w, out_r, err_r = POSIX::Spawn.popen4(*command) + subprocess_thread = Process.detach(pid) - [out, err, $?] + capture_command(in_w, out_r, err_r, subprocess_thread, options) + end + + def capture_command(in_w, out_r, err_r, subprocess_thread, options) + [in_w, out_r, err_r].each(&:binmode) + stdout_reader = Thread.new { out_r.read } + stderr_reader = Thread.new { err_r.read } + begin + in_w.write options[:stdin].to_s + rescue Errno::EPIPE + end + in_w.close + + Timeout.timeout(MiniMagick.timeout) { subprocess_thread.join } + + [stdout_reader.value, stderr_reader.value, subprocess_thread.value] + rescue Timeout::Error => error + Process.kill("TERM", subprocess_thread.pid) + raise error + ensure + [out_r, err_r].each(&:close) end def log(command, &block) value = nil duration = Benchmark.realtime { value = block.call }