Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions lib/puma/cluster/worker.rb
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,8 @@ def run
# Invoke any worker shutdown hooks so they can prevent the worker
# exiting until any background operations are completed
@config.run_hooks(:before_worker_shutdown, index, @log_writer, @hook_data)

Process.kill "SIGTERM", master if server.idle_timeout_reached
Copy link

Choose a reason for hiding this comment

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

Missing exception handling for dead master process signal

Low Severity

The Process.kill "SIGTERM", master call lacks exception handling for Errno::ESRCH, which is raised when the target process no longer exists. If the master process dies between the check_pipe detection and this line (a narrow race window), an unhandled exception occurs. The rest of the codebase consistently handles this exception for Process.kill calls (e.g., in worker_handle.rb). Adding rescue Errno::ESRCH would align with existing patterns.

Fix in Cursor Fix in Web

ensure
@worker_write << "t#{Process.pid}\n" rescue nil
@worker_write.close
Expand Down
2 changes: 1 addition & 1 deletion lib/puma/runner.rb
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ def start_control

app = Puma::App::Status.new @launcher, token

# A Reactor is not created aand nio4r is not loaded when 'queue_requests: false'
# A Reactor is not created and nio4r is not loaded when 'queue_requests: false'
# Use `nil` for events, no hooks in control server
control = Puma::Server.new app, nil,
{ min_threads: 0, max_threads: 1, queue_requests: false, log_writer: @log_writer }
Expand Down
10 changes: 9 additions & 1 deletion lib/puma/server.rb
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ class Server
attr_reader :events
attr_reader :min_threads, :max_threads # for #stats
attr_reader :requests_count # @version 5.0.0
attr_reader :idle_timeout_reached

# @todo the following may be deprecated in the future
attr_reader :auto_trim_time, :early_hints, :first_data_timeout,
Expand Down Expand Up @@ -117,6 +118,8 @@ def initialize(app, events = nil, options = {})
@precheck_closing = true

@requests_count = 0

@idle_timeout_reached = false
end

def inherit_binder(bind)
Expand Down Expand Up @@ -328,7 +331,11 @@ def handle_servers
begin
ios = IO.select sockets, nil, nil, (shutting_down? ? 0 : @idle_timeout)
unless ios
@status = :stop unless shutting_down?
unless shutting_down?
@idle_timeout_reached = true
@status = :stop
end

break
end

Expand Down Expand Up @@ -367,6 +374,7 @@ def handle_servers
@queue_requests = false
@reactor.shutdown
end

graceful_shutdown if @status == :stop || @status == :restart
rescue Exception => e
@log_writer.unknown_error e, nil, "Exception handling servers"
Expand Down
14 changes: 14 additions & 0 deletions test/test_integration_cluster.rb
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,20 @@ def test_worker_timeout
RUBY
end

def test_idle_timeout
cli_server "-w #{workers} test/rackup/hello.ru", config: "idle_timeout 1"

get_worker_pids # wait for workers to boot

connect

sleep 1.15

assert_raises Errno::ECONNREFUSED, "Connection refused" do
connect
end
end

def test_worker_index_is_with_in_options_limit
skip_unless_signal_exist? :TERM

Expand Down
12 changes: 12 additions & 0 deletions test/test_integration_single.rb
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,18 @@ def test_puma_debug_loaded_exts
cli_pumactl 'stop'
end

def test_idle_timeout
cli_server "test/rackup/hello.ru", config: "idle_timeout 1"

connect

sleep 1.15

assert_raises Errno::ECONNREFUSED, "Connection refused" do
connect
end
end

def test_pre_existing_unix_after_idle_timeout
skip_unless :unix

Expand Down