Trapping signals with Signal.trap
For long-running processes, it's often desirable to trap signals sent by the operating system to gracefully handle shutdown, reload configuration, or handle other lifecycle events. In Ruby this couldn't be easier with Signal.trap:
jobs = Array.new(10_000, &:itself)
shutdown = false
Signal.trap("INT") do
puts
puts "Shutting down gracefully after the next job is completed..."
shutdown = true
end
puts "Running... Press Ctrl+C to exit."
until shutdown
job = jobs.shift
# Simulate doing some work
sleep 0.5
puts "Completed job #{job}"
end
Example output:
Running... Press Ctrl+C to exit.
Completed job 0
Completed job 1
Completed job 2
Completed job 3
Completed job 4
^C
Shutting down gracefully after the next job is completed...
Completed job 5
The most common signals to trap are INT (sent when the user presses Ctrl+C) and TERM (sent by default when you run kill <pid>). You can also trap HUP to reload configuration without shutting down, or USR1 and USR2 for custom application-specific signals.
A full list of supported signals can be found by looking at the Signal.list hash.
You can also ignore signals by passing "IGNORE" as a second argument. This can be useful to prevent termination on unsupported signals, e.g. Signal.trap("HUP", "IGNORE").
History
Signal.trap shipped in Ruby 1.8.0 in 2003.
Originally, Ruby provided signal trapping via Kernel#trap, to mimic the shell command. This method is still around, but Signal.trap reads more clearly in my opinion.
The underlying idea comes from Unix signal handling, in particular the C signal / sigaction APIs and the shell trap command.