Class: Mediakit::Process::Runner
- Inherits:
-
Object
- Object
- Mediakit::Process::Runner
- Defined in:
- lib/mediakit/process/runner.rb
Defined Under Namespace
Classes: CommandNotFoundError, IOWatcher, TimeoutError, TimeoutTimer
Constant Summary collapse
- DEFAULT_READ_TIMEOUT_INTERVAL =
30
Instance Attribute Summary collapse
-
#logger ⇒ Object
readonly
Returns the value of attribute logger.
Instance Method Summary collapse
- #build_command(bin, *args) ⇒ Object
- #build_command_without_options(bin, *args) ⇒ Object
- #force_kill_process(pid) ⇒ Object
-
#initialize(timeout: nil, nice: 0, logger: Logger.new(STDOUT)) ⇒ Runner
constructor
A new instance of Runner.
- #run(bin, *args) ⇒ Object
- #run_loop ⇒ Object
- #setup_watchers(stderr) ⇒ Object
- #teardown_watchers ⇒ Object
- #wait(stdout, stderr, wait_thread) ⇒ Object
Constructor Details
#initialize(timeout: nil, nice: 0, logger: Logger.new(STDOUT)) ⇒ Runner
20 21 22 23 24 |
# File 'lib/mediakit/process/runner.rb', line 20 def initialize(timeout: nil, nice: 0, logger: Logger.new(STDOUT)) @timeout = timeout @nice = nice @logger = logger || Mediakit::Utils::NullLogger.new end |
Instance Attribute Details
#logger ⇒ Object (readonly)
Returns the value of attribute logger.
18 19 20 |
# File 'lib/mediakit/process/runner.rb', line 18 def logger @logger end |
Instance Method Details
#build_command(bin, *args) ⇒ Object
65 66 67 68 69 70 71 72 |
# File 'lib/mediakit/process/runner.rb', line 65 def build_command(bin, *args) command = (bin, *args) if @nice == 0 command else "nice -n #{ShellEscape.escape(@nice.to_s)} sh -c \"#{command}\"" end end |
#build_command_without_options(bin, *args) ⇒ Object
74 75 76 77 |
# File 'lib/mediakit/process/runner.rb', line 74 def (bin, *args) escaped_args = ShellEscape.escape(*args) "#{bin} #{escaped_args}" end |
#force_kill_process(pid) ⇒ Object
104 105 106 107 108 |
# File 'lib/mediakit/process/runner.rb', line 104 def force_kill_process(pid) ::Process.kill('SIGKILL', pid) rescue Errno::ESRCH => e logger.warn("fail SIGKILL pid=#{pid} - #{e.message}, #{e.backtrace.join("\n")}") end |
#run(command, *args) ⇒ Object #run(command, args) ⇒ Object
34 35 36 37 38 39 40 41 42 43 44 45 |
# File 'lib/mediakit/process/runner.rb', line 34 def run(bin, *args) command = build_command(bin, *args) begin stdin, stdout, stderr, wait_thread = Open3.popen3(command) stdin.close exit_status, output, error_output = wait(stdout, stderr, wait_thread) rescue Errno::ENOENT => e raise(CommandNotFoundError, "Can't find command - #{command}, #{e.meessage}") end [exit_status, output, error_output] end |
#run_loop ⇒ Object
87 88 89 90 91 92 93 |
# File 'lib/mediakit/process/runner.rb', line 87 def run_loop @loop.run rescue => e # workaround for ambiguous RuntimeError logger.warn(e.) logger.warn(e.backtrace.join("\n")) end |
#setup_watchers(stderr) ⇒ Object
79 80 81 82 83 84 85 |
# File 'lib/mediakit/process/runner.rb', line 79 def setup_watchers(stderr) @timer = @timeout ? TimeoutTimer.new(@timeout, Thread.current) : nil @out_watcher = IOWatcher.new(stderr) { |data| @timer.update if @timer; logger.info(data); } @loop = Coolio::Loop.new @out_watcher.attach(@loop) @timer.attach(@loop) if @timer end |
#teardown_watchers ⇒ Object
95 96 97 98 99 100 101 102 |
# File 'lib/mediakit/process/runner.rb', line 95 def teardown_watchers @loop.watchers.each { |w| w.detach if w.attached? } @out_watcher.close if @out_watcher && !@out_watcher.closed? @loop.stop if @loop.has_active_watchers? rescue RuntimeError => e logger.warn(e.) logger.warn(e.backtrace.join("\n")) end |
#wait(stdout, stderr, wait_thread) ⇒ Object
47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
# File 'lib/mediakit/process/runner.rb', line 47 def wait(stdout, stderr, wait_thread) begin setup_watchers(stderr) loop_thread = Thread.new { run_loop } wait_thread.join exit_status = (wait_thread.value.exitstatus == 0) output = stdout.read rescue Timeout::Error => error force_kill_process(wait_thread.pid) raise(error) ensure teardown_watchers loop_thread.join if loop_thread end [exit_status, output, @out_watcher.data] end |