class RFlow::ChildProcess
Encapsulates a child process being managed by RFlow
.
Constants
- SIGINFO
Symbolic constant for
SIGINFO
as this is only defined on BSD and not in Ruby.
Attributes
The name of the child process. @return [String]
The PID of the child process. @return [Fixnum]
Public Class Methods
Public Instance Methods
Called when the child process needs to be shut down, before it dies. Clears signal handlers. @param signal [String] SIG*, whichever signal caused the shutdown @return [void]
# File lib/rflow/child_process.rb, line 65 def shutdown!(signal) RFlow.logger.info "Shutting down due to #{signal}" unhandle_signals end
Launch another process to execute the child. The parent process retains the original worker object (with pid and IPC pipe) to allow for process management. Parent will return once the child starts; child will update its process name, detach from the process group, set up signal handlers, and execute {run_child_process}; when that returns, it will exit with return code 0. @return [void]
# File lib/rflow/child_process.rb, line 31 def spawn! establish_child_pipe drop_database_connections @pid = fork if @pid return_after_child_starts else run_child_process end end
Protected Instance Methods
# File lib/rflow/child_process.rb, line 44 def run_child_process @child_pipe_w.close register_logging_context update_process_name detach_process_group handle_signals RFlow.logger.info "#{@role} started" run_process exit 0 ensure unhandle_signals end
# File lib/rflow/child_process.rb, line 58 def run_process; end
Private Instance Methods
detach from parent process group so shutdown remains orderly (prevent signals from being delivered to entire group)
# File lib/rflow/child_process.rb, line 99 def detach_process_group Process.setpgid(0, 0) end
Holding database connections over the fork causes problems. Instead, let them be automatically restored after the fork.
# File lib/rflow/child_process.rb, line 78 def drop_database_connections ::ActiveRecord::Base.clear_all_connections! end
# File lib/rflow/child_process.rb, line 71 def establish_child_pipe @child_pipe_r, @child_pipe_w = IO.pipe [@child_pipe_r, @child_pipe_w].each {|io| io.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC) } end
# File lib/rflow/child_process.rb, line 103 def handle_signals Signal.trap 'SIGCHLD', 'DEFAULT' # make sure child process can run subshells ['SIGTERM', 'SIGINT', 'SIGQUIT', 'SIGHUP'].each do |signal| trap_signal(signal) do shutdown! signal exit! 0 end end trap_signal 'SIGUSR1' do RFlow.logger.reopen end trap_signal 'SIGUSR2' do RFlow.logger.toggle_log_level end trap_signal SIGINFO do RFlow.logger.dump_threads end end
# File lib/rflow/child_process.rb, line 87 def register_logging_context # arrange for child's name to appear in log messages RFlow.logger.add_logging_context sprintf("%-#{RFlow.logger.context_width}s", @name) end
# File lib/rflow/child_process.rb, line 82 def return_after_child_starts @child_pipe_r.close self end
# File lib/rflow/child_process.rb, line 132 def trap_signal(signal) # Log4r and traps don't mix, so we need to put it in another thread context = RFlow.logger.clone_logging_context Signal.trap signal do Thread.new do RFlow.logger.apply_logging_context context yield end.join end end
# File lib/rflow/child_process.rb, line 126 def unhandle_signals ['SIGTERM', 'SIGINT', 'SIGQUIT', 'SIGHUP', 'SIGCHLD', 'SIGUSR1', 'SIGUSR2', SIGINFO].each do |signal| Signal.trap signal, 'DEFAULT' end end
# File lib/rflow/child_process.rb, line 92 def update_process_name # set the visible process name to match the child's name $0 += " #{@name}" end