Are system signals ( INT, TERM, etc) sent to all threads at the same
time? I thought that it would cascade from the parent to the
children, but that doesn’t seem to be the case.
I’m running Sinatra in a child thread. Sinatra traps INT (
Control-C), shuts down the web server, but never exits the thread.
Trapping the interrupt at the parent process doesn’t seem to have any
effect.
Are system signals ( INT, TERM, etc) sent to all threads at the same
time? I thought that it would cascade from the parent to the
children, but that doesn’t seem to be the case.
I’m running Sinatra in a child thread. Sinatra traps INT (
Control-C), shuts down the web server, but never exits the thread.
Trapping the interrupt at the parent process doesn’t seem to have any
effect.
The JVM sets up a separate thread to handle signals, so registering
for those events basically just registers a callback to run on that
thread.
Can you investigate a bit more how MRI does this? We are somewhat
limited in how we work within the confines of the JVM, but if there’s
something we can improve we’ll do it.
Signal handlers are global to the whole process and all its threads.
there is only one signal handler per signal defined and the last
installed handler will the one called. note that calling Kernel.trap
returns the previously defined handler.
As far as your specific problem with Sinatra, I tried this minimal
setup and I can’t reproduce your problem (JRuby 1.6.7.2). Can you
reproduce the problem in the smallest possible setup and share it?
class Controller < Sinatra::Base
set :port, 8080
get ‘/’ do
“Hello World”
end
end
t = Thread.new do
Controller.run!
end
t.join
One thing I can say is that Sinatra initializes itself and install its
signal handlers in a at_exit block.
Just wanted to add about the Sinatra at_exit initialization: this is
when you use Sinatra the “default” way, not by calling
Sinatra::Base#run! as in my example. If you want to install your own
INT signal handler in my example, you could, by installing it just
before the t.join statement, but make sure to wait a bit to make sure
Sinatra completes its initialization code before setting up your own
trap.
threads << Thread.new {
until MySinatraTest.running?
sleep 1
end
trap(“INT”) {puts “trapped in subthread”; exit}
puts “ok, trap registered”
}
threads.each do |thread|
thread.join
end
In case this helps someone else, you can also just restore the default
signal handler instead of punting it to another thread by using the
‘DEFAULT’ parameter to Signal.trap, e.g.
sleep 1 until MySinatraTest.running?
trap(:INT, ‘DEFAULT’)
On Fri, Jul 13, 2012 at 2:20 PM, Colin Surprenant [email protected] wrote:
Signal handlers are global to the whole process and all its threads.
there is only one signal handler per signal defined and the last
installed handler will the one called
This ended up being the key. Since Sinatra has a bit of startup time,
it was the last thread to register the trap. Once the INT was
signaled, Sinatra would shut down, but other threads would continue to
execute.
I ended up adding an additional thread that waited for Sinatra to
start up (Sinatra.running? == true), then registers a new Signal trap
to override Sinatra’s trap.
Code:
require ‘sinatra/base’
class MySinatraTest < Sinatra::Base
get ‘/’ do
“hello world”
end
end
threads = Array.new()
threads << Thread.new { MySinatraTest.run! }
threads << Thread.new {
until MySinatraTest.running?
sleep 1
end
trap(“INT”) {puts “trapped in subthread”; exit}
puts “ok, trap registered”
}
threads.each do |thread|
thread.join
end
Thanks for the help,
Chris
This forum is not affiliated to the Ruby language, Ruby on Rails framework, nor any Ruby applications discussed here.