1
0
Fork 0
mirror of https://github.com/mperham/sidekiq.git synced 2022-11-09 13:52:34 -05:00

Refactor signal handling to use a pipe

This commit removes the need to poll a global array for received signals every
second by introducing an internal pipe(2).

The writing end of the pipe is being used in the block trapping the signals.
Whenever a signal is received, the signal's name gets written to the pipe.

Instead of polling for received signals, the code now uses select(2) to
check whether the internal pipe has been written to and can be read from.
select(2) is blocking until one of the passed file descriptors is ready for
reading/writing or has an exception, which means the code does not need to use
`sleep 1` anymore.

So when a signal is sent to the sidekiq process, the signal name gets written to
the internal pipe, select(2) returns and the process can react according to the
received signal. After handling a signal, select(2) gets called again and blocks
until receiving another signal.
This commit is contained in:
Thorsten Ball 2013-03-08 14:39:33 +01:00
parent 3f483c30a2
commit dfc7e35b11

View file

@ -1,11 +1,11 @@
$sidekiq_signals = [] $self_read, $self_write = IO.pipe
# Signal handlers should do as little as humanly possible # Signal handlers should do as little as humanly possible
# and defer all work to a non-trap context. We'll have # and defer all work to a non-trap context. We'll have
# the main thread poll for signals and handle them there. # the main thread poll for signals and handle them there.
%w(INT TERM USR1 USR2 TTIN).each do |sig| %w(INT TERM USR1 USR2 TTIN).each do |sig|
trap sig do trap sig do
$sidekiq_signals << sig $self_write.puts(sig)
end end
end end
@ -69,9 +69,9 @@ module Sidekiq
end end
launcher.run launcher.run
while true while readable_io = IO.select([$self_read])
handle_signals signal = readable_io.first[0].gets.strip
sleep 1 handle_signal(signal)
end end
rescue Interrupt rescue Interrupt
logger.info 'Shutting down' logger.info 'Shutting down'
@ -82,40 +82,38 @@ module Sidekiq
end end
end end
def handle_signals def handle_signal(sig)
while sig = $sidekiq_signals.shift Sidekiq.logger.debug "Got #{sig} signal"
Sidekiq.logger.debug "Got #{sig} signal" case sig
case sig when 'INT'
when 'INT' if Sidekiq.options[:profile]
if Sidekiq.options[:profile] result = RubyProf.stop
result = RubyProf.stop printer = RubyProf::GraphHtmlPrinter.new(result)
printer = RubyProf::GraphHtmlPrinter.new(result) File.open("profile.html", 'w') do |f|
File.open("profile.html", 'w') do |f| printer.print(f, :min_percent => 1)
printer.print(f, :min_percent => 1)
end
end end
# Handle Ctrl-C in JRuby like MRI end
# http://jira.codehaus.org/browse/JRUBY-4637 # Handle Ctrl-C in JRuby like MRI
raise Interrupt # http://jira.codehaus.org/browse/JRUBY-4637
when 'TERM' raise Interrupt
# Heroku sends TERM and then waits 10 seconds for process to exit. when 'TERM'
raise Interrupt # Heroku sends TERM and then waits 10 seconds for process to exit.
when 'USR1' raise Interrupt
Sidekiq.logger.info "Received USR1, no longer accepting new work" when 'USR1'
launcher.manager.async.stop Sidekiq.logger.info "Received USR1, no longer accepting new work"
when 'USR2' launcher.manager.async.stop
if Sidekiq.options[:logfile] when 'USR2'
Sidekiq.logger.info "Received USR2, reopening log file" if Sidekiq.options[:logfile]
Sidekiq::Logging.initialize_logger(Sidekiq.options[:logfile]) Sidekiq.logger.info "Received USR2, reopening log file"
end Sidekiq::Logging.initialize_logger(Sidekiq.options[:logfile])
when 'TTIN' end
Thread.list.each do |thread| when 'TTIN'
Sidekiq.logger.info "Thread TID-#{thread.object_id.to_s(36)} #{thread['label']}" Thread.list.each do |thread|
if thread.backtrace Sidekiq.logger.info "Thread TID-#{thread.object_id.to_s(36)} #{thread['label']}"
Sidekiq.logger.info thread.backtrace.join("\n") if thread.backtrace
else Sidekiq.logger.info thread.backtrace.join("\n")
Sidekiq.logger.info "<no backtrace available>" else
end Sidekiq.logger.info "<no backtrace available>"
end end
end end
end end