From 5ae77e8ce4c439cdfdf1cbaee6a74fcda0b468b1 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Mon, 27 Jan 2014 16:49:14 +0000 Subject: fix races/error handling in worker SIGQUIT handler This protects us from two problems: 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 can happen as a fake signal from the master could be handled and a real signal from an outside user is sent to us (e.g. from unicorn-worker-killer) or if a user uses the killall(1) command. --- 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 -- cgit v1.2.3-24-ge0c7