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

progress[R]
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.

tms[R]
Benchmark::Tms

The time mesured of execution in the runner thread.

Public Class Methods

dead?(pid) click to toggle source
# File lib/long-command-runner/runner.rb, line 161
def self.dead?(pid)
  Process.kill(0, pid)
  false
rescue Errno::ESRCH
  true
end
kill_children(signal, pid, children) click to toggle source
# 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
new(command, opts = {}, &on_input) click to toggle source

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

kill(signal) click to toggle source

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
launch() click to toggle source

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
launched?() click to toggle source

Tells if the command has been launched at least once.

# File lib/long-command-runner/runner.rb, line 57
def launched?
  !@container.nil?
end
output(separator = "\n") click to toggle source

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
output_error(separator = "\n") click to toggle source

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
pid() click to toggle source

Get the pid of the process launched

@return [Integer | nil]

# File lib/long-command-runner/runner.rb, line 100
def pid
  @container.pid
end
running?() click to toggle source

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
status() click to toggle source

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
time_real() click to toggle source

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
time_sys() click to toggle source

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
time_user() click to toggle source

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() click to toggle source

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

manage_progress(new_line) click to toggle source
# 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
on_newline(nl_stdout, nl_stderr) click to toggle source
# 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