Hey all, I'm trying to diagnose an issue with another user over
private email.
It seems io.close in the trap(:QUIT) handler of the worker process is
causing an IOError, which means an IO in the readers array already got
closed somehow. This shouldn't happen, and CRuby 2.x doesn't seem
to interrupt itself inside signal handlers[1].
Below is what I have so far...
Worst case is we're not any worse off than before; but we
could be hiding another bug with the "rescue nil" on io.close.
An extra set of eyes would be appreciated.
Pushed to the llerrloop branch of git://bogomips.org/unicorn.git
Also on rubygems.org: gem install --pre -v 4.8.0.1.g10a2 unicorn
[1] - however other Ruby runtimes may.
Subject: [PATCH] http_server: safer SIGQUIT handler for worker
This protects us from two potential errors:
1) we (or our app) somehow called IO#close on one of the sockets
we listen on without removing it from the readers array.
We'll ignore IOErrors from IO#close and assume we wanted to
close it.
2) our SIGQUIT handler is interrupted by itself. This is currently
not possible with (MRI) Ruby 2.x, but it may happen in other
implementations (as it does in normal C code).
---
lib/unicorn/http_server.rb | 11 +++++++++--
1 file changed, 9 insertions(+), 2 deletions(-)
diff --git a/lib/unicorn/http_server.rb b/lib/unicorn/http_server.rb
index ae8ad13..2052d53 100644
--- a/lib/unicorn/http_server.rb
+++ b/lib/unicorn/http_server.rb
@@ -591,6 +591,13 @@ class Unicorn::HttpServer
EXIT_SIGS = [ :QUIT, :TERM, :INT ]
WORKER_QUEUE_SIGS = QUEUE_SIGS - EXIT_SIGS
+ def nuke_listeners!(readers)
+ # only called from the worker, ordering is important here
+ tmp = readers.dup
+ readers.replace([false]) # ensure worker does not continue ASAP
+ tmp.each { |io| io.close rescue nil } # break out of IO.select
+ end
+
# gets rid of stuff the worker has no business keeping track of
# to free some resources and drops all sig handlers.
# traps for USR1, USR2, and HUP may be set in the after_fork Proc
@@ -618,7 +625,7 @@ class Unicorn::HttpServer
@after_fork = @listener_opts = @orig_app = nil
readers = LISTENERS.dup
readers << worker
- trap(:QUIT) { readers.each { |io| io.close }.replace([false]) }
+ trap(:QUIT) { nuke_listeners!(readers) }
readers
end
@@ -677,7 +684,7 @@ class Unicorn::HttpServer
worker.tick = Time.now.to_i
ret = IO.select(readers, nil, nil, @timeout) and ready = ret[0]
rescue => e
- redo if nr < 0
+ redo if nr < 0 && readers[0]
Unicorn.log_error(@logger, "listen loop error", e) if
readers[0]
end while readers[0]
end
--
1.8.5.3.368.gab0bcec
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying