From: Joe Leo Date: 2013-01-05T09:46:17+09:00 Subject: Re: GServer in Ruby 2.0.0 --e89a8f3baba328387904d27fd2e4 Content-Type: text/plain; charset=ISO-8859-1 Wow, this is a really interesting (and effective) solution. Reading the docs on IO.pipe (http://www.ruby-doc.org/core-1.9.3/IO.html) 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. For what it's worth, I've filed my issue with ruby-core here: http://bugs.ruby-lang.org/issues/7648. It hasn't gained any traction yet, but if you want to post your own experience with Queue#enq it might be an addendum to this bug report or a new bug altogether. Thanks for your help! This was educational. Joe On Fri, Jan 4, 2013 at 8:44 AM, Sean O'Halpin wrote: > On Tue, Jan 1, 2013 at 4:20 PM, Joe Leo wrote: > > I'm working on updating a gem to be compatible with Ruby 2.0 and am > > currently using Ruby 2.0.0-preview2. I'm running into trouble with a > > class which inherits from GServer and I think it's related to this bug > and > > subsequent patch. Please look at the gist: > > https://gist.github.com/4424479 > > > > 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' > > > > 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 > > servers = [MyServer.new] > launcher = Launcher.new(servers) > > # create self-pipe > p_read, p_write = IO.pipe > > Thread.start(launcher, p_read) do |l, pr| > pr.read # blocks until write end closed > pr.close > l.stop > exit > end > > launcher.start > trap("SIGINT") { p_write.close } # close pipe to signal thread > launcher.join > > --e89a8f3baba328387904d27fd2e4 Content-Type: text/html; charset=ISO-8859-1 Content-Transfer-Encoding: quoted-printable

Wow, this is a really interesting (and effe= ctive) solution. Reading the docs on IO.pipe (http://www.ruby-doc.org/core-1.9.3/IO.html) t= his 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 r= eceives a SIGTERM. (As a counter example, calling "exit!" inside = the original trap context in my gist would have successfully killed my prog= ram without throwing an exception but would not have cleaned up the utilize= d 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 on= es successfully, I'll go with it.=A0

For what it's worth, I've filed my issue with = ruby-core here:=A0http://= bugs.ruby-lang.org/issues/7648. It hasn't gained any traction yet, = but if you want to post your own experience with Queue#enq it might be an a= ddendum to this bug report or a new bug altogether.

Thanks for = your help! This was educational.

=
Joe

On Fri, Ja= n 4, 2013 at 8:44 AM, Sean O'Halpin <sean.ohalpin@gmail.com&g= t; wrote:
On Tue, Jan 1, 2013 at 4:2= 0 PM, Joe Leo <joseph.leo3@gmai= l.com> wrote:
> I'm working on updating a gem to be compatible with Ruby 2.0 and a= m
> currently using Ruby 2.0.0-preview2. I'm running into trouble with= a
> class which inherits from GServer and I think it's related to this= bug and
> subsequent patch. Please look at the gist:
> https://= gist.github.com/4424479
>
> Using Ruby <=3D 1.9.3, when sending an interrupt signal this code w= ould
> 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)=
> =A0 =A0 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
=A0 def initialize(port=3D10001, *args)
=A0 =A0 super(port, *args)
=A0 end
=A0 def serve(io)
=A0 =A0 io.puts(Time.now.to_s)
=A0 end
end

class Launcher
=A0 def initialize(servers)
=A0 =A0 @servers =3D servers
=A0 end

=A0 def start
=A0 =A0 @servers.each { |server| server.start }
=A0 end

=A0 def join
=A0 =A0 @servers.each { |server| server.join }
=A0 end

=A0 def stop
=A0 =A0 @servers.each { |server| server.stop }
=A0 end
end

servers =3D [MyServer.new]
launcher =3D Launcher.new(servers)

# create self-pipe
p_read, p_write =3D IO.pipe

Thread.start(launcher, p_read) do |l, pr|
=A0 pr.read # blocks until write end closed
=A0 pr.close
=A0 l.stop
=A0 exit
end

launcher.start
trap("SIGINT") { p_write.close } # close pipe to signal thread launcher.join


--e89a8f3baba328387904d27fd2e4--