Using Ruby <= 1.9.3, when sending an interrupt signal this code would
exit cleanly. Using Ruby 2.0, an exception is thrown on launcher.stop:
~/.rvm/rubies/ruby-2.0.0-preview2/lib/ruby/2.0.0/gserver.rb:116:in synchronize': can't be called from trap context (ThreadError) from ~/.rvm/rubies/ruby-2.0.0-preview2/lib/ruby/2.0.0/gserver.rb:116:in stop’
As you can see, I am not explicitly calling ‘join’ in the trap context
but ‘synchronize’ is called as part of GServer#close
gserver.rb
114 # Stop the server
115 def stop
116 @connectionsMutex.synchronize {
117 if @tcpServerThread
118 @tcpServerThread.raise “stop”
119 end
120 }
121 end
synchronize': can't be called from trap context (ThreadError) from ~/.rvm/rubies/ruby-2.0.0-preview2/lib/ruby/2.0.0/gserver.rb:116:in stop’
I can confirm this is new behaviour in 2.0.0-preview2 - it also
happens when you use Queue#enq from within a trap handler (which is an
issue for me unfortunately).
You could use a variant of the self-pipe trick to signal a thread as
in the example below. Regards, Sean.
CODE
require “gserver”
example server
class MyServer < GServer
def initialize(port=10001, *args)
super(port, *args)
end
def serve(io)
io.puts(Time.now.to_s)
end
end
class Launcher
def initialize(servers) @servers = servers
end
def start @servers.each { |server| server.start }
end
def join @servers.each { |server| server.join }
end
def stop @servers.each { |server| server.stop }
end
end
Wow, this is a really interesting (and effective) solution. Reading the
docs on IO.pipe (Class: IO (Ruby 1.9.3)) this looks
pretty safe. And it works on Ruby 1.9.3 and 2.0.0-preview2.
My objective was to clean up my resources when the program receives a
SIGTERM. (As a counter example, calling “exit!” inside the original trap
context in my gist would have successfully killed my program without
throwing an exception but would not have cleaned up the utilized
resources.) There is the ominous “does not work on all systems” warning
with this method, but if I can test it out on some of the major ones
successfully, I’ll go with it.