Class: BackgroundProcess
- Inherits:
-
Object
- Object
- BackgroundProcess
- Defined in:
- lib/background_process/background_process.rb
Direct Known Subclasses
Defined Under Namespace
Modules: IOHelpers
Instance Attribute Summary collapse
-
#pid ⇒ Object
readonly
Returns the value of attribute pid.
-
#stderr ⇒ Object
readonly
Returns the value of attribute stderr.
-
#stdin ⇒ Object
readonly
Returns the value of attribute stdin.
-
#stdout ⇒ Object
readonly
Returns the value of attribute stdout.
Class Method Summary collapse
-
.run(*command_with_args) ⇒ Object
Run a command, connecting it's IO streams (stdin, sterr, stdout) via IO pipes, which are not tty IO streams.
-
.sanitize_command(*args) ⇒ Object
It's protected.
Instance Method Summary collapse
-
#detect(which = :both, timeout = nil, &block) ⇒ Object
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.
-
#exitstatus ⇒ Object
Waits for the process to terminate, and then returns the exit status.
-
#initialize(pid, stdin, stdout, stderr = nil) ⇒ BackgroundProcess
constructor
Initialize a BackgroundProcess task.
-
#interrupt ⇒ Object
Sends the interrupt signal to the process.
-
#kill(signal = 'TERM') ⇒ Object
send a signal to the process.
-
#running? ⇒ Boolean
asks the operating system is the process still exists.
-
#wait(timeout = nil) ⇒ Object
waits for the process to finish.
Constructor Details
#initialize(pid, stdin, stdout, stderr = nil) ⇒ BackgroundProcess
Initialize a BackgroundProcess task. Don't do this. Use BackgroundProcess.run or BackgroundProcess.run_pty instead
5 6 7 8 |
# 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 |
Instance Attribute Details
#pid ⇒ Object (readonly)
Returns the value of attribute pid.
2 3 4 |
# File 'lib/background_process/background_process.rb', line 2 def pid @pid end |
#stderr ⇒ Object (readonly)
Returns the value of attribute stderr.
2 3 4 |
# File 'lib/background_process/background_process.rb', line 2 def stderr @stderr end |
#stdin ⇒ Object (readonly)
Returns the value of attribute stdin.
2 3 4 |
# File 'lib/background_process/background_process.rb', line 2 def stdin @stdin end |
#stdout ⇒ Object (readonly)
Returns the value of attribute stdout.
2 3 4 |
# File 'lib/background_process/background_process.rb', line 2 def stdout @stdout end |
Class Method Details
.run(*command_with_args) ⇒ Object
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.
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
# 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 |
.sanitize_command(*args) ⇒ Object
It's protected. What do you care? :P
102 103 104 105 106 |
# 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 |
Instance Method Details
#detect(which = :both, timeout = nil, &block) ⇒ Object
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.
95 96 97 98 |
# 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 ⇒ Object
Waits for the process to terminate, and then returns the exit status
84 85 86 |
# File 'lib/background_process/background_process.rb', line 84 def exitstatus wait && wait.exitstatus end |
#interrupt ⇒ Object
Sends the interrupt signal to the process. The equivalent of pressing control-C in it.
58 59 60 |
# File 'lib/background_process/background_process.rb', line 58 def interrupt kill('INT') end |
#kill(signal = 'TERM') ⇒ Object
send a signal to the process. If the processes and running, do nothing. Valid signals are those in Signal.list. Default is "TERM"
50 51 52 53 54 55 |
# 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? ⇒ Boolean
asks the operating system is the process still exists.
63 64 65 66 67 68 69 |
# 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) ⇒ Object
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)
74 75 76 77 78 79 80 81 |
# 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 |