Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Non-overriding signal handlers #2767

Closed
13 changes: 7 additions & 6 deletions lib/puma/cluster.rb
Expand Up @@ -5,6 +5,7 @@
require 'puma/plugin'
require 'puma/cluster/worker_handle'
require 'puma/cluster/worker'
require 'puma/signal'

require 'time'

Expand Down Expand Up @@ -266,7 +267,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

Expand All @@ -280,23 +281,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.
Expand Down Expand Up @@ -396,7 +397,7 @@ def run

spawn_workers

Signal.trap "SIGINT" do
Puma::Signal.trap "SIGINT" do
stop
end

Expand Down
16 changes: 10 additions & 6 deletions lib/puma/cluster/worker.rb
Expand Up @@ -30,10 +30,14 @@ 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" do
end

Puma::Signal.trap "SIGCHLD" do |signal|
raise SignalException, signal
end

Thread.new do
Thread.new do
Puma.set_thread_name "wrkr check"
@check_pipe.wait_readable
log "! Detected parent died, dying"
Expand All @@ -55,7 +59,7 @@ def run
@launcher.config.run_hooks :before_worker_boot, index, @launcher.events

begin
server = @server ||= start_server
server = @server ||= start_server
rescue Exception => e
log "! Unable to start worker"
log e.backtrace[0]
Expand All @@ -69,7 +73,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
Expand All @@ -96,7 +100,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
Expand Down
13 changes: 7 additions & 6 deletions lib/puma/launcher.rb
Expand Up @@ -6,6 +6,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
Expand Down Expand Up @@ -454,7 +455,7 @@ def generate_restart_data

def setup_signals
begin
Signal.trap "SIGUSR2" do
Puma::Signal.trap "SIGUSR2" do
restart
end
rescue Exception
Expand All @@ -463,7 +464,7 @@ def setup_signals

unless Puma.jruby?
begin
Signal.trap "SIGUSR1" do
Puma::Signal.trap "SIGUSR1" do
phased_restart
end
rescue Exception
Expand All @@ -472,7 +473,7 @@ def setup_signals
end

begin
Signal.trap "SIGTERM" do
Puma::Signal.trap "SIGTERM" do
graceful_stop

raise(SignalException, "SIGTERM") if @options[:raise_exception_on_sigterm]
Expand All @@ -482,15 +483,15 @@ def setup_signals
end

begin
Signal.trap "SIGINT" do
Puma::Signal.trap "SIGINT" do
stop
end
rescue Exception
log "*** SIGINT not implemented, signal based gracefully stopping unavailable!"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This exception handling could also be moved to Puma::Signal module

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep, I'll consider it if the solution makes sense at all 😄

end

begin
Signal.trap "SIGHUP" do
Puma::Signal.trap "SIGHUP" do
if @runner.redirected_io?
@runner.redirect_io
else
Expand All @@ -503,7 +504,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|
@events.log name
@events.log backtrace.map { |bt| " #{bt}" }
Expand Down
42 changes: 42 additions & 0 deletions lib/puma/signal.rb
@@ -0,0 +1,42 @@
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
nateberkopec marked this conversation as resolved.
Show resolved Hide resolved

# Signal.trap that does not replace
# the existing puma handlers
def trap(sig, &handler)
name = signame(sig)
::Signal.trap(name) do
invoke_custom_signal_handlers(name)
yield handler
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