Class: Proxy::Dynflow::ProcessManager
- Inherits:
-
Object
- Object
- Proxy::Dynflow::ProcessManager
- Defined in:
- lib/smart_proxy_dynflow/process_manager.rb
Overview
An abstraction for managing local processes.
It can be used to:
-
spawn a local process
-
track its lifecycle
-
communicate with it through its standard input, output and error
-
step through the execution one event at a time or start the child process and wait until it finishes
Instance Attribute Summary collapse
-
#pid ⇒ nil, Integer
readonly
Process id of the child process, nil if the process was not started yet, -1 if the process could not be started.
-
#status ⇒ nil, Integer
readonly
Exit status of the child process.
-
#stderr ⇒ Proxy::Dynflow::IOBuffer
readonly
IOBuffer buffering reads from child process’ standard error.
-
#stdin ⇒ Proxy::Dynflow::IOBuffer
readonly
IOBuffer buffering writes to child process’ standard input.
-
#stdout ⇒ Proxy::Dynflow::IOBuffer
readonly
IOBuffer buffering reads from child process’ standard output.
Instance Method Summary collapse
-
#close ⇒ void
Makes the process manager close all the pipes it may have opened to communicate with the child process.
-
#done? ⇒ true, false
Determines whether the child process of the process manager already finished.
-
#initialize(command) ⇒ ProcessManager
constructor
A new instance of ProcessManager.
-
#on_stderr(&block) ⇒ void
Sets block to be executed each time data is read from child process’ standard error.
-
#on_stdout(&block) ⇒ void
Sets block to be executed each time data is read from child process’ standard output.
-
#process(timeout: nil) ⇒ void
Runs a single iteration of the manager’s processing loop.
-
#run! ⇒ ProcessManager
Starts the process manager and runs it until it finishes.
-
#start! ⇒ void
Starts the child process.
-
#started? ⇒ true, false
Determines whether the process manager already forked off its child process.
Constructor Details
Instance Attribute Details
#pid ⇒ nil, Integer (readonly)
Process id of the child process, nil if the process was not started yet, -1 if the process could not be started
51 52 53 |
# File 'lib/smart_proxy_dynflow/process_manager.rb', line 51 def pid @pid end |
#status ⇒ nil, Integer (readonly)
Exit status of the child process. nil if the child process has not finished yet, 255 if the process could not be started
51 52 53 |
# File 'lib/smart_proxy_dynflow/process_manager.rb', line 51 def status @status end |
#stderr ⇒ Proxy::Dynflow::IOBuffer (readonly)
IOBuffer buffering reads from child process’ standard error
51 52 53 |
# File 'lib/smart_proxy_dynflow/process_manager.rb', line 51 def stderr @stderr end |
#stdin ⇒ Proxy::Dynflow::IOBuffer (readonly)
IOBuffer buffering writes to child process’ standard input
51 52 53 |
# File 'lib/smart_proxy_dynflow/process_manager.rb', line 51 def stdin @stdin end |
#stdout ⇒ Proxy::Dynflow::IOBuffer (readonly)
IOBuffer buffering reads from child process’ standard output
51 52 53 |
# File 'lib/smart_proxy_dynflow/process_manager.rb', line 51 def stdout @stdout end |
Instance Method Details
#close ⇒ void
This method returns an undefined value.
Makes the process manager close all the pipes it may have opened to communicate with the child process
159 160 161 |
# File 'lib/smart_proxy_dynflow/process_manager.rb', line 159 def close [@stdin, @stdout, @stderr].each(&:close) end |
#done? ⇒ true, false
Determines whether the child process of the process manager already finished
104 105 106 |
# File 'lib/smart_proxy_dynflow/process_manager.rb', line 104 def done? started? && !status.nil? end |
#on_stderr(&block) ⇒ void
This method returns an undefined value.
Sets block to be executed each time data is read from child process’ standard error
152 153 154 |
# File 'lib/smart_proxy_dynflow/process_manager.rb', line 152 def on_stderr(&block) @stderr.on_data(&block) end |
#on_stdout(&block) ⇒ void
This method returns an undefined value.
Sets block to be executed each time data is read from child process’ standard output
145 146 147 |
# File 'lib/smart_proxy_dynflow/process_manager.rb', line 145 def on_stdout(&block) @stdout.on_data(&block) end |
#process(timeout: nil) ⇒ void
This method returns an undefined value.
Runs a single iteration of the manager’s processing loop. It waits until either:
-
data is available in pipes connected to the child process’ standard output or error
-
there is pending data to be written and the pipe connected to the child process’ standard input is writable
-
a timeout is reached
After the wait, all pending data is read and written.
If all the pipes connected to the child process are closed, it marks the execution as complete and performs cleanup.
120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 |
# File 'lib/smart_proxy_dynflow/process_manager.rb', line 120 def process(timeout: nil) raise 'Cannot process until the manager is started' unless started? writers = [@stdin].reject { |buf| buf.empty? || buf.closed? } readers = [@stdout, @stderr].reject(&:closed?) if readers.empty? && writers.empty? finish return end # Even though the FDs are still open, the child might have exited already pid, status = Process.waitpid2(@pid, Process::WNOHANG) timeout = 1 if pid ready_readers, ready_writers = IO.select(readers, writers, nil, timeout) (ready_readers || []).each(&:read_available!) (ready_writers || []).each(&:write_available!) finish(status) if pid end |
#run! ⇒ ProcessManager
Starts the process manager and runs it until it finishes
65 66 67 68 69 |
# File 'lib/smart_proxy_dynflow/process_manager.rb', line 65 def run! start! unless started? process until done? self end |
#start! ⇒ void
This method returns an undefined value.
Starts the child process. It creates 3 pipes for communicating with the child process and the forks it. The process manager is considered done if the child process cannot be started.
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 |
# File 'lib/smart_proxy_dynflow/process_manager.rb', line 76 def start! in_read, in_write = IO.pipe out_read, out_write = IO.pipe err_read, err_write = IO.pipe @stdin.io = in_write @stdout.io = out_read @stderr.io = err_read @pid = spawn(*@command, :in => in_read, :out => out_write, :err => err_write) [in_read, out_write, err_write].each(&:close) rescue Errno::ENOENT => e [in_read, in_write, out_read, out_write, err_read, err_write].each(&:close) @pid = -1 @status = 255 @stderr.add_data(e.) end |
#started? ⇒ true, false
Determines whether the process manager already forked off its child process
97 98 99 |
# File 'lib/smart_proxy_dynflow/process_manager.rb', line 97 def started? !pid.nil? end |