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 - [email protected]
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying

Reply via email to