class PerfectQueue::Multiprocess::ChildProcessMonitor
Attributes
pid[R]
Public Class Methods
new(log, pid, rpipe)
click to toggle source
# File lib/perfectqueue/multiprocess/child_process_monitor.rb, line 23 def initialize(log, pid, rpipe) @log = log @pid = pid @rpipe = rpipe @last_heartbeat = Time.now.to_i @kill_start_time = nil @last_kill_time = nil @kill_immediate = false @rbuf = '' end
Public Instance Methods
check_heartbeat(limit)
click to toggle source
# File lib/perfectqueue/multiprocess/child_process_monitor.rb, line 38 def check_heartbeat(limit) @rpipe.read_nonblock(1024, @rbuf) @last_heartbeat = Time.now.to_i return true rescue Errno::EINTR, Errno::EAGAIN return Time.now.to_i - @last_heartbeat <= limit end
cleanup()
click to toggle source
# File lib/perfectqueue/multiprocess/child_process_monitor.rb, line 98 def cleanup @rpipe.close unless @rpipe.closed? end
killing_status()
click to toggle source
# File lib/perfectqueue/multiprocess/child_process_monitor.rb, line 62 def killing_status if @kill_start_time if @kill_immediate return true else return false end else return nil end end
send_signal(sig)
click to toggle source
# File lib/perfectqueue/multiprocess/child_process_monitor.rb, line 102 def send_signal(sig) begin Process.kill(sig, @pid) rescue Errno::ESRCH, Errno::EPERM # TODO log? end end
start_killing(immediate, delay=0)
click to toggle source
# File lib/perfectqueue/multiprocess/child_process_monitor.rb, line 46 def start_killing(immediate, delay=0) if immediate && !@kill_immediate @kill_immediate = true # escalation elsif @kill_start_time return end now = Time.now.to_i if delay == 0 @last_kill_time = @kill_start_time = now kill_children(now, nil) else @last_kill_time = @kill_start_time = now + delay end end
try_join(kill_interval, graceful_kill_limit)
click to toggle source
# File lib/perfectqueue/multiprocess/child_process_monitor.rb, line 74 def try_join(kill_interval, graceful_kill_limit) return nil unless @kill_start_time begin if Process.waitpid(@pid, Process::WNOHANG) @log.info "Processor exited and joined pid=#{@pid}" return true end rescue Errno::ECHILD # SIGCHLD is trapped in Supervisor#install_signal_handlers @log.info "Processor exited pid=#{@pid}" return true end # resend signal now = Time.now.to_i if @last_kill_time + kill_interval <= now kill_children(now, graceful_kill_limit) @last_kill_time = now end return false end
Private Instance Methods
collect_child_pids(ppid_pids, results, parent_pid)
click to toggle source
# File lib/perfectqueue/multiprocess/child_process_monitor.rb, line 137 def collect_child_pids(ppid_pids, results, parent_pid) if pids = ppid_pids[parent_pid] pids.each {|pid| results << pid collect_child_pids(ppid_pids, results, pid) } end results end
get_ppid_pids_map()
click to toggle source
# File lib/perfectqueue/multiprocess/child_process_monitor.rb, line 124 def get_ppid_pids_map ppid_pids = {} # {ppid => [pid]} `ps axo pid,ppid`.each_line do |line| if m = /^\s*(\d+)\s+(\d+)\s*$/.match(line) (ppid_pids[m[2].to_i] ||= []) << m[1].to_i end end return ppid_pids # We can ignore errors but not necessary #rescue # return {} end
kill_children(now, graceful_kill_limit)
click to toggle source
# File lib/perfectqueue/multiprocess/child_process_monitor.rb, line 111 def kill_children(now, graceful_kill_limit) immediate = @kill_immediate || (graceful_kill_limit && @kill_start_time + graceful_kill_limit < now) if immediate pids = collect_child_pids(get_ppid_pids_map, [@pid], @pid) pids.reverse_each {|pid| kill_process(pid, true) } else kill_process(@pid, false) end end
kill_process(pid, immediate)
click to toggle source
# File lib/perfectqueue/multiprocess/child_process_monitor.rb, line 147 def kill_process(pid, immediate) begin if immediate @log.debug "sending SIGKILL to pid=#{pid} for immediate stop" Process.kill(:KILL, pid) else @log.debug "sending SIGTERM to pid=#{pid} for graceful stop" Process.kill(:TERM, pid) end rescue Errno::ESRCH, Errno::EPERM # TODO log? end end