class BackgroundProcess

Attributes

pid[R]
stderr[R]
stdin[R]
stdout[R]

Public Class Methods

new(pid, stdin, stdout, stderr = nil) click to toggle source

Initialize a BackgroundProcess task. Don't do this. Use BackgroundProcess.run or BackgroundProcess.run_pty instead

# File lib/background_process/background_process.rb, line 5
def initialize(pid, stdin, stdout, stderr = nil)
  @pid, @stdin, @stdout, @stderr = pid, stdin, stdout, stderr
  ObjectSpace.define_finalizer(self) { kill }
end
run(*command_with_args) click to toggle source

Run a command, connecting it's IO streams (stdin, sterr, stdout) via IO pipes, which are not tty IO streams.

Because of this, some programs (like ruby) will buffer their output and only make it available when it's explicitely flushed (with IO#flush or when the buffer gets full). This behavior can be overridden by setting the streams to sync, like this:

STDOUT.sync, STDERR.sync = true, true

If you can't control the program and have it explicitly flush its output when it should, or you can't tell the streams to run in sync mode, see PTYBackgroundProcess.run for a workaround.

# File lib/background_process/background_process.rb, line 24
def self.run(*command_with_args)
  command = sanitize_command(command_with_args)
  child_stdin, parent_stdin = IO::pipe
  parent_stdout, child_stdout = IO::pipe
  parent_stderr, child_stderr = IO::pipe

  pid = Kernel.fork do
    [parent_stdin, parent_stdout, parent_stderr].each { |io| io.close }

    STDIN.reopen(child_stdin)
    STDOUT.reopen(child_stdout)
    STDERR.reopen(child_stderr)

    [child_stdin, child_stdout, child_stderr].each { |io| io.close }

    exec command
  end

  [child_stdin, child_stdout, child_stderr].each { |io| io.close }
  parent_stdin.sync = true

  new(pid, parent_stdin, parent_stdout, parent_stderr)
end

Protected Class Methods

sanitize_command(*args) click to toggle source

It's protected. What do you care? :P

# File lib/background_process/background_process.rb, line 102
def self.sanitize_command(*args)
  command_and_args = args.flatten
  return command_and_args.first if command_and_args.length == 1
  command_and_args.map { |p| p.gsub(' ', '\ ') }.join(" ")
end

Public Instance Methods

detect(which = :both, timeout = nil, &block) click to toggle source

Calls block each time a line is available in the specified output buffer(s) and returns the first non-false value By default, both stdout and stderr are monitored.

Args:

  • which: which streams to monitor. valid values are :stdout, :stderr, or :both.

  • timeout: Total time in seconds to run detect for. If result not found within this time, abort and return nil. Pass nil for no timeout.

  • &block: the block to call. If block takes two arguments, it will pass both the stream that received the input (an instance of IO, not the symbol), and the line read from the buffer.

# File lib/background_process/background_process.rb, line 95
def detect(which = :both, timeout = nil, &block)
  streams = select_streams(which)
  BackgroundProcess::IOHelpers.detect(streams, timeout, &block)
end
exitstatus() click to toggle source

Waits for the process to terminate, and then returns the exit status

# File lib/background_process/background_process.rb, line 84
def exitstatus
  wait && wait.exitstatus
end
interrupt() click to toggle source

Sends the interrupt signal to the process. The equivalent of pressing control-C in it.

# File lib/background_process/background_process.rb, line 58
def interrupt
  kill('INT')
end
kill(signal = 'TERM') click to toggle source

send a signal to the process. If the processes and running, do nothing. Valid signals are those in Signal.list. Default is “TERM”

# File lib/background_process/background_process.rb, line 50
def kill(signal = 'TERM')
  if running?
    Process.kill(Signal.list[signal], @pid)
    true
  end
end
running?() click to toggle source

asks the operating system is the process still exists.

# File lib/background_process/background_process.rb, line 63
def running?
  return false unless @pid
  Process.getpgid(@pid)
  true
rescue Errno::ESRCH
  false
end
wait(timeout = nil) click to toggle source

waits for the process to finish. Freeze the process so it can rest in peace. You should call this on every background job you create to avoid a flood of zombie processes. (Processes won't go away until they are waited on)

# File lib/background_process/background_process.rb, line 74
def wait(timeout = nil)
  @exit_status ||= Timeout.timeout(timeout) do
    Process.wait(@pid)
    $?
  end
rescue Timeout::Error
  nil
end

Protected Instance Methods

select_streams(which) click to toggle source
# File lib/background_process/background_process.rb, line 108
def select_streams(which)
  case which
  when :stdout then [stdout]
  when :stderr then [stderr]
  when :both   then [stdout, stderr]
  else raise(ArgumentError, "invalid stream specification: #{which}")
  end.compact
end