class EY::Serverside::Spawner
Public Class Methods
new()
click to toggle source
# File lib/engineyard-serverside/spawner.rb, line 13 def initialize @poll_period = 0.5 @children = [] end
run(cmd, shell, server = nil)
click to toggle source
# File lib/engineyard-serverside/spawner.rb, line 7 def self.run(cmd, shell, server = nil) s = new s.add(cmd, shell, server) s.run.first end
Public Instance Methods
add(cmd, shell, server = nil)
click to toggle source
# File lib/engineyard-serverside/spawner.rb, line 18 def add(cmd, shell, server = nil) @children << Child.new(cmd, shell, server) end
run()
click to toggle source
# File lib/engineyard-serverside/spawner.rb, line 22 def run @child_by_fd = {} @child_by_pid = {} @children.each do |child| pid, stdout_fd, stderr_fd = child.spawn @child_by_pid[pid] = child @child_by_fd[stdout_fd] = child @child_by_fd[stderr_fd] = child end while @child_by_pid.any? process wait end @children.map { |child| child.result } end
Protected Instance Methods
process()
click to toggle source
# File lib/engineyard-serverside/spawner.rb, line 43 def process read_fds = @child_by_pid.values.map {|child| child.ios }.flatten.compact ra, _, _ = IO.select(read_fds, [], [], @poll_period) process_readable(ra) if ra end
process_readable(ra)
click to toggle source
# File lib/engineyard-serverside/spawner.rb, line 49 def process_readable(ra) ra.each do |fd| child = @child_by_fd[fd] if !child raise "Select returned unknown fd: #{fd.inspect}" end begin if buf = fd.sysread(4096) child.append_to_buffer(fd, buf) else raise "sysread() returned nil" end rescue SystemCallError, EOFError => e @child_by_fd.delete(fd) child.close(fd) end end end
wait()
click to toggle source
# File lib/engineyard-serverside/spawner.rb, line 69 def wait possible_children = true just_reaped = [] while possible_children begin pid, status = Process::waitpid2(-1, Process::WNOHANG) if pid.nil? possible_children = false elsif child = @child_by_pid.delete(pid) child.finished status just_reaped << child elsif pid == -1 # waitpid encountered an error (as defined in linux waitpid manpage) # apparently it can leak through ruby's waitpid abstraction raise "Fatal error encountered while waiting for a child process to exit. waitpid2 returned: [#{pid.inspect}, #{status.inspect}].\nExpected one of children: #{@child_by_pid.keys.inspect}" else raise "Unknown pid returned from waitpid2 => #{pid.inspect}, #{status.inspect}.\nExpected one of children: #{@child_by_pid.keys.inspect}" end rescue Errno::ECHILD possible_children = false end end # We may have waited on a child before reading all its output. Collect those missing bits. No blocking. if just_reaped.any? read_fds = just_reaped.map {|child| child.ios }.flatten.compact ra, _, _ = IO.select(read_fds, nil, nil, 0) process_readable(ra) if ra end end