class LCR::Runner
This class aims to manage an external process through a parallele excecution thread. This means it will not stop the main thread of your program.
Your process can output a progress indicator of the matching the format of PERCENT_INDICATOR
constant.
It is possible to re-launch the same command, the out/err buffers won't be emptied before running unless asked.
Constants
- PERCENT_INDICATOR
The constant used to get the percetange of completion of the command.
Attributes
- Float
-
Get the mesured progress in percentage.
This is just the result of parsing stdout lines with {PERCENT_INDICATOR}. So this percentage is comming from the thread not this library.
- Benchmark::Tms
-
The time mesured of execution in the runner thread.
Public Class Methods
# File lib/long-command-runner/runner.rb, line 161 def self.dead?(pid) Process.kill(0, pid) false rescue Errno::ESRCH true end
# File lib/long-command-runner/runner.rb, line 168 def self.kill_children(signal, pid, children) n = Process.kill(signal, pid) sleep 0.1 children.keys.reject { |c_pid| dead? c_pid }.each do |c_pid| # the not dead n += kill_children(signal, c_pid, children[c_pid]) end n end
Initializer takes the command as a plain string. @param [String] command The command to run (can be a one-line shell script) @option opts [Boolean] :do_progress_on Set to `'stderr'`, if you want to use
stderr to get the progression percentage instead of the default: `'stdout'`. Any other value will deactivate the feature.
You can optionaly pass a block that will be called when the command generate outputs: @yield [nl_stdout, nl_stderr] call when at least a line comes on stdout or stderr. @yieldparam [String | nil] nl_stdout The new line without the return line character.
This may be nil if only a new line has been found on stderr.
@yieldparam [String | nil] nl_stderr The new line without the return line character.
This may be nil if only a new line has been found on stdout.
@yieldreturn [void] It is ignored.
# File lib/long-command-runner/runner.rb, line 39 def initialize(command, opts = {}, &on_input) @command = command @container = nil @launch_lock = Mutex.new @on_input = on_input @progress = 0.0 @tms = nil @do_progress_on = opts[:do_progress_on] || 'stdout' end
Public Instance Methods
Send a signal to the running process.
@return [Integer | nil] The number of signaled process (= 1) or nil
if the Process is no more running.
# File lib/long-command-runner/runner.rb, line 129 def kill(signal) return nil if @container.nil? children = @container.children Runner.kill_children(signal, @container.pid, children) rescue Errno::ESRCH nil end
Actually launch the process.
# File lib/long-command-runner/runner.rb, line 62 def launch @bench_thread = Thread.new do @tms = Benchmark.measure do @container = Container.new(@command) @container.wait end end Thread.pass while @container.nil? @line_reader = LineReader.new([@container.stdout, @container.stderr]) do |*new_lines| on_newline(*new_lines) end @reader_thr = Thread.new { @line_reader.read } Thread.pass end
Tells if the command has been launched at least once.
# File lib/long-command-runner/runner.rb, line 57 def launched? !@container.nil? end
Get the output lines as separated lines
@param [String] separator the separator to put between lines.
by default it will be `"\n"`
@return [String]
# File lib/long-command-runner/runner.rb, line 83 def output(separator = "\n") @line_reader[0].join(separator) end
Get the error output lines as separated lines
@param [String] separator the separator to put between lines.
by default it will be `"\n"`
@return [String]
# File lib/long-command-runner/runner.rb, line 93 def output_error(separator = "\n") @line_reader[1].join(separator) end
Get the pid of the process launched
@return [Integer | nil]
# File lib/long-command-runner/runner.rb, line 100 def pid @container.pid end
Is the last launched command is still running.
# File lib/long-command-runner/runner.rb, line 50 def running? return false if @container.nil? @container.running? end
Get the status of the process without blocking.
@return [Process:Status | nil] The exit status of the process if it is finished.
if the Process isn't finished it return nil.
# File lib/long-command-runner/runner.rb, line 119 def status return nil if @container.nil? @container.status end
Old get the time really spend ('real' part of `time` command) DEPRECATED: use tms.real access now.
# File lib/long-command-runner/runner.rb, line 140 def time_real warn '[DEPRECATION] use :tms instead of :time_real' return nil if @tms.nil? @tms.real end
Get the total time spent in system space (sum of tms.cstime + tms.stime)
# File lib/long-command-runner/runner.rb, line 155 def time_sys return nil if @tms.nil? @tms.cstime + @tms.stime end
Get the total time spent in user space (sum of tms.cutime + tms.utime)
# File lib/long-command-runner/runner.rb, line 148 def time_user return nil if @tms.nil? @tms.cutime + @tms.utime end
Wait and return the process exit status. This method is blocking until the process if finished.
@return [Process::Status]
# File lib/long-command-runner/runner.rb, line 108 def wait @bench_thread.join @reader_thr.join sleep 0.01 until @line_reader.streams.all?(&:eof?) @container.status end
Private Instance Methods
# File lib/long-command-runner/runner.rb, line 190 def manage_progress(new_line) match = new_line.match PERCENT_INDICATOR return unless match @progress = match[2].to_f end
# File lib/long-command-runner/runner.rb, line 180 def on_newline(nl_stdout, nl_stderr) nl_progress = if @do_progress_on == 'stdout' nl_stdout elsif @do_progress_on == 'stderr' nl_stderr end manage_progress(nl_progress) unless nl_progress.nil? @on_input&.call(nl_stdout, nl_stderr) end