diff --git a/lib/puma/cluster.rb b/lib/puma/cluster.rb index 5c5e2c726a..07f2538d05 100644 --- a/lib/puma/cluster.rb +++ b/lib/puma/cluster.rb @@ -5,6 +5,7 @@ require 'puma/plugin' require 'puma/cluster/worker_handle' require 'puma/cluster/worker' +require 'puma/signal' require 'time' @@ -288,7 +289,7 @@ def fork_worker! # of the signals handlers as small as possible. def setup_signals if @options[:fork_worker] - Signal.trap "SIGURG" do + Puma::Signal.trap "SIGURG" do fork_worker! end @@ -302,23 +303,23 @@ def setup_signals end end - Signal.trap "SIGCHLD" do + Puma::Signal.trap "SIGCHLD" do wakeup! end - Signal.trap "TTIN" do + Puma::Signal.trap "TTIN" do @options[:workers] += 1 wakeup! end - Signal.trap "TTOU" do + Puma::Signal.trap "TTOU" do @options[:workers] -= 1 if @options[:workers] >= 2 wakeup! end master_pid = Process.pid - Signal.trap "SIGTERM" do + Puma::Signal.trap "SIGTERM" do # The worker installs their own SIGTERM when booted. # Until then, this is run by the worker and the worker # should just exit if they get it. @@ -418,7 +419,7 @@ def run spawn_workers - Signal.trap "SIGINT" do + Puma::Signal.trap "SIGINT" do stop end diff --git a/lib/puma/cluster/worker.rb b/lib/puma/cluster/worker.rb index 10e3a9d856..f921dda610 100644 --- a/lib/puma/cluster/worker.rb +++ b/lib/puma/cluster/worker.rb @@ -30,8 +30,9 @@ def run title += " [#{@options[:tag]}]" if @options[:tag] && !@options[:tag].empty? $0 = title - Signal.trap "SIGINT", "IGNORE" - Signal.trap "SIGCHLD", "DEFAULT" + Puma::Signal.trap "SIGINT", "IGNORE" + + Puma::Signal.trap "SIGCHLD", "DEFAULT" Thread.new do Puma.set_thread_name "wrkr check" @@ -55,7 +56,7 @@ def run @launcher.config.run_hooks(:before_worker_boot, index, @launcher.log_writer) begin - server = @server ||= start_server + server = @server ||= start_server rescue Exception => e log "! Unable to start worker" log e.backtrace[0] @@ -69,7 +70,7 @@ def run if fork_worker restart_server.clear worker_pids = [] - Signal.trap "SIGCHLD" do + Puma::Signal.trap "SIGCHLD" do wakeup! if worker_pids.reject! do |p| Process.wait(p, Process::WNOHANG) rescue true end @@ -96,7 +97,7 @@ def run end end - Signal.trap "SIGTERM" do + Puma::Signal.trap "SIGTERM" do @worker_write << "e#{Process.pid}\n" rescue nil restart_server.clear server.stop diff --git a/lib/puma/launcher.rb b/lib/puma/launcher.rb index 7fc4652d43..a1c3fdb048 100644 --- a/lib/puma/launcher.rb +++ b/lib/puma/launcher.rb @@ -7,6 +7,7 @@ require 'puma/single' require 'puma/const' require 'puma/binder' +require 'puma/signal' module Puma # Puma::Launcher is the single entry point for starting a Puma server based on user @@ -415,7 +416,7 @@ def generate_restart_data def setup_signals begin - Signal.trap "SIGUSR2" do + Puma::Signal.trap "SIGUSR2" do restart end rescue Exception @@ -424,7 +425,7 @@ def setup_signals unless Puma.jruby? begin - Signal.trap "SIGUSR1" do + Puma::Signal.trap "SIGUSR1" do phased_restart end rescue Exception @@ -433,7 +434,7 @@ def setup_signals end begin - Signal.trap "SIGTERM" do + Puma::Signal.trap "SIGTERM" do # Shortcut the control flow in case raise_exception_on_sigterm is true do_graceful_stop @@ -444,7 +445,7 @@ def setup_signals end begin - Signal.trap "SIGINT" do + Puma::Signal.trap "SIGINT" do stop end rescue Exception @@ -452,7 +453,7 @@ def setup_signals end begin - Signal.trap "SIGHUP" do + Puma::Signal.trap "SIGHUP" do if @runner.redirected_io? @runner.redirect_io else @@ -465,7 +466,7 @@ def setup_signals begin unless Puma.jruby? # INFO in use by JVM already - Signal.trap "SIGINFO" do + Puma::Signal.trap "SIGINFO" do thread_status do |name, backtrace| @log_writer.log(name) @log_writer.log(backtrace.map { |bt| " #{bt}" }) diff --git a/lib/puma/signal.rb b/lib/puma/signal.rb new file mode 100644 index 0000000000..4b78841b36 --- /dev/null +++ b/lib/puma/signal.rb @@ -0,0 +1,51 @@ +module Puma + module Signal + module_function + + def prepend_handler(sig, &handler) + name = signame(sig) + custom_signal_handlers[signame] ||= [] + custom_signal_handlers[signame].prepend(handler) + end + + # Signal.trap that does not replace + # the existing puma handlers + def trap(sig, handler_str=nil, &handler) + name = signame(sig) + ::Signal.trap(name) do + invoke_custom_signal_handlers(name) + + if handler.respond_to?(:call) + yield handler + else + # dirty hack to call string handlers + # especially for DEFAULT and SYSTEM_DEFAULT + current_trap = ::Signal.trap(name, handler_str) + Process.kill(name, Process.pid) + ::Signal.trap(name, current_trap) + end + end + end + + private + + def signame(sig) + name = sig.is_a?(Integer) ? ::Signal.signame(sig) : String(sig).sub('SIG', '') + raise ArgumentError, "unsupported signal SIG#{name}" unless ::Signal.list[name] + name + end + module_function :signame + + def invoke_custom_signal_handlers(signame) + Array(custom_signal_handlers[signame]).each do |handler| + yield handler + end + end + module_function :invoke_custom_signal_handlers + + def custom_signal_handlers + @custom_signal_handlers ||= {} + end + module_function :custom_signal_handlers + end +end