about summary refs log tree commit homepage
path: root/lib/unicorn.rb
diff options
context:
space:
mode:
Diffstat (limited to 'lib/unicorn.rb')
-rw-r--r--lib/unicorn.rb106
1 files changed, 45 insertions, 61 deletions
diff --git a/lib/unicorn.rb b/lib/unicorn.rb
index f5c1c8c..4a4e2e1 100644
--- a/lib/unicorn.rb
+++ b/lib/unicorn.rb
@@ -377,7 +377,9 @@ module Unicorn
     # worker.
     def murder_lazy_workers
       WORKERS.each_pair do |pid, worker|
-        Time.now - worker.tempfile.ctime <= @timeout and next
+        stat = worker.tempfile.stat
+        stat.mode == 0100000 and next
+        Time.now - stat.ctime <= @timeout and next
         logger.error "worker=#{worker.nr} PID:#{pid} is too old, killing"
         kill_worker(:KILL, pid) # take no prisoners for @timeout violations
         worker.tempfile.close rescue nil
@@ -414,8 +416,6 @@ module Unicorn
     # once a client is accepted, it is processed in its entirety here
     # in 3 easy steps: read request, call app, write app response
     def process_client(client)
-      # one syscall less than "client.nonblock = false":
-      client.fcntl(Fcntl::F_SETFL, File::RDWR)
       HttpResponse.write(client, @app.call(@request.read(client)))
     # if we get any error, try to write something back to the client
     # assuming we haven't closed the socket, but don't get hung up
@@ -423,20 +423,15 @@ module Unicorn
     # the socket is closed at the end of this function
     rescue EOFError,Errno::ECONNRESET,Errno::EPIPE,Errno::EINVAL,Errno::EBADF
       client.write_nonblock(Const::ERROR_500_RESPONSE) rescue nil
+      client.close rescue nil
     rescue HttpParserError # try to tell the client they're bad
       client.write_nonblock(Const::ERROR_400_RESPONSE) rescue nil
+      client.close rescue nil
     rescue Object => e
       client.write_nonblock(Const::ERROR_500_RESPONSE) rescue nil
+      client.close rescue nil
       logger.error "Read error: #{e.inspect}"
       logger.error e.backtrace.join("\n")
-    ensure
-      begin
-        client.closed? or client.close
-      rescue Object => e
-        logger.error "Client error: #{e.inspect}"
-        logger.error e.backtrace.join("\n")
-      end
-      @request.reset
     end
 
     # gets rid of stuff the worker has no business keeping track of
@@ -475,16 +470,18 @@ module Unicorn
       nr = 0 # this becomes negative if we need to reopen logs
       alive = worker.tempfile # tempfile is our lifeline to the master process
       ready = LISTENERS
-      client = nil
+      t = ti = 0
 
       # closing anything we IO.select on will raise EBADF
       trap(:USR1) { nr = -65536; SELF_PIPE.first.close rescue nil }
       trap(:QUIT) { alive = nil; LISTENERS.each { |s| s.close rescue nil } }
-      [:TERM, :INT].each { |sig| trap(sig) { exit(0) } } # instant shutdown
+      [:TERM, :INT].each { |sig| trap(sig) { exit!(0) } } # instant shutdown
       @logger.info "worker=#{worker.nr} ready"
 
-      while alive
-        reopen_worker_logs(worker.nr) if nr < 0
+      begin
+        nr < 0 and reopen_worker_logs(worker.nr)
+        nr = 0
+
         # we're a goner in @timeout seconds anyways if alive.chmod
         # breaks, so don't trap the exception.  Using fchmod() since
         # futimes() is not available in base Ruby and I very strongly
@@ -493,55 +490,41 @@ module Unicorn
         # changes with chmod doesn't update ctime on all filesystems; so
         # we change our counter each and every time (after process_client
         # and before IO.select).
-        alive.chmod(nr = 0)
+        t == (ti = Time.now.to_i) or alive.chmod(t = ti)
+
+        ready.each do |sock|
+          begin
+            process_client(sock.accept_nonblock)
+            nr += 1
+            t == (ti = Time.now.to_i) or alive.chmod(t = ti)
+          rescue Errno::EAGAIN, Errno::ECONNABORTED
+          end
+          break if nr < 0
+        end
+
+        # make the following bet: if we accepted clients this round,
+        # we're probably reasonably busy, so avoid calling select()
+        # and do a speculative accept_nonblock on every listener
+        # before we sleep again in select().
+        redo unless nr == 0 # (nr < 0) => reopen logs
 
+        master_pid == Process.ppid or return
+        alive.chmod(t = 0)
         begin
-          ready.each do |sock|
-            begin
-              client = begin
-                sock.accept_nonblock
-              rescue Errno::EAGAIN
-                next
-              end
-              process_client(client)
-            rescue Errno::ECONNABORTED
-              # client closed the socket even before accept
-              client.close rescue nil
-            ensure
-              alive.chmod(nr += 1) if client
-              break if nr < 0
-            end
-          end
-          client = nil
-
-          # make the following bet: if we accepted clients this round,
-          # we're probably reasonably busy, so avoid calling select()
-          # and do a speculative accept_nonblock on every listener
-          # before we sleep again in select().
-          if nr != 0 # (nr < 0) => reopen logs
-            ready = LISTENERS
-          else
-            master_pid == Process.ppid or exit(0)
-            alive.chmod(nr += 1)
-            begin
-              # timeout used so we can detect parent death:
-              ret = IO.select(LISTENERS, nil, SELF_PIPE, @timeout) or next
-              ready = ret.first
-            rescue Errno::EINTR
-              ready = LISTENERS
-            rescue Errno::EBADF => e
-              nr < 0 or exit(alive ? 1 : 0)
-            end
-          end
-        rescue SignalException, SystemExit => e
-          raise e
-        rescue Object => e
-          if alive
-            logger.error "Unhandled listen loop exception #{e.inspect}."
-            logger.error e.backtrace.join("\n")
-          end
+          # timeout used so we can detect parent death:
+          ret = IO.select(LISTENERS, nil, SELF_PIPE, @timeout) or redo
+          ready = ret.first
+        rescue Errno::EINTR
+          ready = LISTENERS
+        rescue Errno::EBADF
+          nr < 0 or return
         end
-      end
+      rescue Object => e
+        if alive
+          logger.error "Unhandled listen loop exception #{e.inspect}."
+          logger.error e.backtrace.join("\n")
+        end
+      end while alive
     end
 
     # delivers a signal to a worker and fails gracefully if the worker
@@ -585,6 +568,7 @@ module Unicorn
         @config.reload
         @config.commit!(self)
         kill_each_worker(:QUIT)
+        Unicorn::Util.reopen_logs
         logger.info "done reloading config_file=#{@config.config_file}"
       rescue Object => e
         logger.error "error reloading config_file=#{@config.config_file}: " \