Class: Autorespawn::Slave
- Inherits:
-
Object
- Object
- Autorespawn::Slave
- Defined in:
- lib/autorespawn/slave.rb
Overview
Representation of an autorespawn-aware subprocess that is started by a Manager
Slaves have two roles: the one of discovery (what are the commands that need to be started) and the one of
Direct Known Subclasses
Instance Attribute Summary collapse
-
#cmdline ⇒ Object
readonly
The command line of the subprocess.
-
#name ⇒ Object
readonly
The slave’s name.
-
#pid ⇒ nil, Integer
readonly
Pid the PID of the current process or of the last process if it is finished.
-
#program_id ⇒ Object
readonly
The currently known program ID.
-
#result_buffer ⇒ String
readonly
private
The result data as received.
-
#result_r ⇒ IO
readonly
private
The result I/O.
-
#spawn_env ⇒ Object
readonly
Environment that should be set in the subprocess.
-
#spawn_options ⇒ Object
readonly
Options that should be passed to Kernel.spawn.
-
#status ⇒ Process::Status
readonly
The exit status of the last run.
-
#subcommands ⇒ Array<String>
readonly
A list of commands that this slave requests.
Instance Method Summary collapse
-
#finished(status) ⇒ Object
private
Announce that the slave already finished, with the given exit status.
-
#finished? ⇒ Boolean
Whether the slave has already ran, and is finished.
-
#initialize(*cmdline, name: nil, seed: ProgramID.new, env: Hash.new, **spawn_options) ⇒ Slave
constructor
A new instance of Slave.
- #inspect ⇒ Object
-
#join ⇒ Object
Wait for the slave to terminate and call #finished.
-
#kill(signal = 'TERM', join: true) ⇒ Object
Kill the slave.
-
#needs_spawn? ⇒ Boolean
Whether this slave would need to be spawned, either because it has never be, or because the program ID changed.
-
#read_queued_result ⇒ Object
private
Queue any pending result data sent by the slave.
-
#register_files(files, search_path = program_id.ruby_load_path, ignore_not_found: true) ⇒ Object
Register files on the program ID.
-
#running? ⇒ Boolean
Whether the slave is running.
-
#spawn ⇒ Integer
Start the slave.
-
#success? ⇒ Boolean
Whether the slave behaved properly.
- #to_s ⇒ Object
Constructor Details
#initialize(*cmdline, name: nil, seed: ProgramID.new, env: Hash.new, **spawn_options) ⇒ Slave
Returns a new instance of Slave.
46 47 48 49 50 51 52 53 54 55 56 57 58 |
# File 'lib/autorespawn/slave.rb', line 46 def initialize(*cmdline, name: nil, seed: ProgramID.new, env: Hash.new, **) @name = name @program_id = seed.dup @cmdline = cmdline @needs_spawn = true @spawn_env = env @spawn_options = @subcommands = Array.new @pid = nil @status = nil @result_r = nil @result_buffer = nil end |
Instance Attribute Details
#cmdline ⇒ Object (readonly)
The command line of the subprocess
17 18 19 |
# File 'lib/autorespawn/slave.rb', line 17 def cmdline @cmdline end |
#name ⇒ Object (readonly)
The slave’s name
It is an arbitrary object useful for reporting/tracking
13 14 15 |
# File 'lib/autorespawn/slave.rb', line 13 def name @name end |
#pid ⇒ nil, Integer (readonly)
Returns pid the PID of the current process or of the last process if it is finished. It is non-nil only after #spawn has been called.
25 26 27 |
# File 'lib/autorespawn/slave.rb', line 25 def pid @pid end |
#program_id ⇒ Object (readonly)
The currently known program ID
15 16 17 |
# File 'lib/autorespawn/slave.rb', line 15 def program_id @program_id end |
#result_buffer ⇒ String (readonly)
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Returns the result data as received.
40 41 42 |
# File 'lib/autorespawn/slave.rb', line 40 def result_buffer @result_buffer end |
#result_r ⇒ IO (readonly)
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Returns the result I/O.
36 37 38 |
# File 'lib/autorespawn/slave.rb', line 36 def result_r @result_r end |
#spawn_env ⇒ Object (readonly)
Environment that should be set in the subprocess
19 20 21 |
# File 'lib/autorespawn/slave.rb', line 19 def spawn_env @spawn_env end |
#spawn_options ⇒ Object (readonly)
Options that should be passed to Kernel.spawn
21 22 23 |
# File 'lib/autorespawn/slave.rb', line 21 def @spawn_options end |
#status ⇒ Process::Status (readonly)
Returns the exit status of the last run. Is nil while the process is running.
28 29 30 |
# File 'lib/autorespawn/slave.rb', line 28 def status @status end |
#subcommands ⇒ Array<String> (readonly)
Returns a list of commands that this slave requests.
31 32 33 |
# File 'lib/autorespawn/slave.rb', line 31 def subcommands @subcommands end |
Instance Method Details
#finished(status) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Announce that the slave already finished, with the given exit status
164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 |
# File 'lib/autorespawn/slave.rb', line 164 def finished(status) @status = status read_queued_result begin @subcommands, file_list = Marshal.load(result_buffer) @success = true rescue ArgumentError # "Marshal data too short" @subcommands = Array.new file_list = Array.new @success = false end modified = program_id.register_files(file_list) @program_id = program_id.slice(file_list) if !modified.empty? @needs_spawn = true end result_r.close modified end |
#finished? ⇒ Boolean
Whether the slave has already ran, and is finished
125 126 127 |
# File 'lib/autorespawn/slave.rb', line 125 def finished? pid && status end |
#inspect ⇒ Object
60 61 62 |
# File 'lib/autorespawn/slave.rb', line 60 def inspect "#<Autorespawn::Slave #{object_id.to_s(16)} #{cmdline.join(" ")}>" end |
#join ⇒ Object
Wait for the slave to terminate and call #finished
142 143 144 145 |
# File 'lib/autorespawn/slave.rb', line 142 def join _, status = Process.waitpid2(pid) finished(status) end |
#kill(signal = 'TERM', join: true) ⇒ Object
Kill the slave
134 135 136 137 138 139 |
# File 'lib/autorespawn/slave.rb', line 134 def kill(signal = 'TERM', join: true) Process.kill signal, pid if join self.join end end |
#needs_spawn? ⇒ Boolean
Whether this slave would need to be spawned, either because it has never be, or because the program ID changed
115 116 117 |
# File 'lib/autorespawn/slave.rb', line 115 def needs_spawn? @needs_spawn || !status || program_id.changed? end |
#read_queued_result ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Queue any pending result data sent by the slave
187 188 189 190 191 192 |
# File 'lib/autorespawn/slave.rb', line 187 def read_queued_result while true result_buffer << result_r.read_nonblock(1024) end rescue IO::WaitReadable, EOFError end |
#register_files(files, search_path = program_id.ruby_load_path, ignore_not_found: true) ⇒ Object
Register files on the program ID
(see ProgramID#register_files)
69 70 71 |
# File 'lib/autorespawn/slave.rb', line 69 def register_files(files, search_path = program_id.ruby_load_path, ignore_not_found: true) program_id.register_files(files, search_path, ignore_not_found: ignore_not_found) end |
#running? ⇒ Boolean
Whether the slave is running
120 121 122 |
# File 'lib/autorespawn/slave.rb', line 120 def running? pid && !status end |
#spawn ⇒ Integer
Start the slave
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 |
# File 'lib/autorespawn/slave.rb', line 76 def spawn if running? raise AlreadyRunning, "cannot call #spawn on #{self}: already running" end initial_r, initial_w = IO.pipe result_r, result_w = IO.pipe env = self.spawn_env.merge( SLAVE_INITIAL_STATE_ENV => initial_r.fileno.to_s, SLAVE_RESULT_ENV => result_w.fileno.to_s) program_id.refresh @needs_spawn = false pid = Kernel.spawn(env, *cmdline, initial_r => initial_r, result_w => result_w, **) initial_r.close result_w.close Marshal.dump([name, program_id], initial_w) @pid = pid @status = nil @result_buffer = '' @result_r = result_r pid rescue Exception => e if pid Process.kill 'TERM', pid end result_r.close if result_r && !result_r.closed? raise ensure initial_r.close if initial_r && !initial_r.closed? initial_w.close if initial_w && !initial_r.closed? result_w.close if result_w && !result_w.closed? end |
#success? ⇒ Boolean
Whether the slave behaved properly
This does not indicate whether the slave’s intended work has been done, only that it produced the data expected by Autorespawn. To check the child’s success w.r.t. its execution, check #status
152 153 154 155 156 157 |
# File 'lib/autorespawn/slave.rb', line 152 def success? if !status raise NotFinished, "called {#success?} on a #{pid ? 'running' : 'non-started'} child" end @success end |
#to_s ⇒ Object
64 |
# File 'lib/autorespawn/slave.rb', line 64 def to_s; inspect end |