about summary refs log tree commit homepage
path: root/lib/yahns/server.rb
diff options
context:
space:
mode:
authorEric Wong <e@80x24.org>2013-10-26 02:45:49 +0000
committerEric Wong <e@80x24.org>2013-10-26 02:45:49 +0000
commit50b9493b07023a8d6502e620657fa209b6aa74ef (patch)
treed191cf061b5bd7b24f28209da170acebde614e65 /lib/yahns/server.rb
parent5d5377e094745ee76cd066d2244c52b40647d1cc (diff)
downloadyahns-50b9493b07023a8d6502e620657fa209b6aa74ef.tar.gz
We'll hit SIGCHLD if our reexec process fails on us, so the
non-MP server must handle it, too.  We discovered this bug
while porting the PID file renaming changes from unicorn.
Diffstat (limited to 'lib/yahns/server.rb')
-rw-r--r--lib/yahns/server.rb64
1 files changed, 51 insertions, 13 deletions
diff --git a/lib/yahns/server.rb b/lib/yahns/server.rb
index ae3c1d7..8490c5a 100644
--- a/lib/yahns/server.rb
+++ b/lib/yahns/server.rb
@@ -4,7 +4,8 @@
 require_relative 'queue_quitter'
 
 class Yahns::Server # :nodoc:
-  QUEUE_SIGS = [ :WINCH, :QUIT, :INT, :TERM, :USR1, :USR2, :HUP, :TTIN, :TTOU ]
+  QUEUE_SIGS = [ :WINCH, :QUIT, :INT, :TERM, :USR1, :USR2, :HUP, :TTIN, :TTOU,
+                 :CHLD ]
   attr_accessor :daemon_pipe
   attr_accessor :logger
   attr_writer :worker_processes
@@ -52,12 +53,11 @@ class Yahns::Server # :nodoc:
     # setup signal handlers before writing pid file in case people get
     # trigger happy and send signals as soon as the pid file exists.
     QUEUE_SIGS.each { |sig| trap(sig) { sqwakeup(sig) } }
-    self.pid = @config.value(:pid) # write pid file
     bind_new_listeners!
+    self.pid = @config.value(:pid) # write pid file
     if @worker_processes
       require 'yahns/server_mp'
       extend Yahns::ServerMP
-      mp_init
     end
     self
   end
@@ -100,6 +100,21 @@ class Yahns::Server # :nodoc:
     (set_names - cur_names).each { |addr| listen(addr) }
   end
 
+  def clobber_pid(path)
+    unlink_pid_safe(@pid) if @pid
+    if path
+      fp = begin
+        tmp = "#{File.dirname(path)}/#{rand}.#$$"
+        File.open(tmp, File::RDWR|File::CREAT|File::EXCL, 0644)
+      rescue Errno::EEXIST
+        retry
+      end
+      fp.syswrite("#$$\n")
+      File.rename(fp.path, path)
+      fp.close
+    end
+  end
+
   # sets the path for the PID file of the master process
   def pid=(path)
     if path
@@ -114,18 +129,18 @@ class Yahns::Server # :nodoc:
                              "(or pid=#{path} is stale)"
       end
     end
-    unlink_pid_safe(@pid) if @pid
 
-    if path
-      fp = begin
-        tmp = "#{File.dirname(path)}/#{rand}.#$$"
-        File.open(tmp, File::RDWR|File::CREAT|File::EXCL, 0644)
-      rescue Errno::EEXIST
-        retry
+    # rename the old pid if possible
+    if @pid && path
+      begin
+        File.rename(@pid, path)
+      rescue Errno::ENOENT, Errno::EXDEV
+        # a user may have accidentally removed the original,
+        # obviously cross-FS renames don't work, either.
+        clobber_pid(path)
       end
-      fp.syswrite("#$$\n")
-      File.rename(fp.path, path)
-      fp.close
+    else
+      clobber_pid(path)
     end
     @pid = path
   end
@@ -369,6 +384,8 @@ class Yahns::Server # :nodoc:
     case sig = @sig_queue.shift
     when :QUIT, :TERM, :INT
       return quit_enter(alive)
+    when :CHLD
+      reap_all
     when :USR1
       usr1_reopen('')
     when :USR2
@@ -396,4 +413,25 @@ class Yahns::Server # :nodoc:
   ensure
     quit_finish
   end
+
+  # reaps all unreaped workers/reexec processes
+  def reap_all
+    begin
+      wpid, status = Process.waitpid2(-1, Process::WNOHANG)
+      wpid or return
+      if @reexec_pid == wpid
+        @logger.error "reaped #{status.inspect} exec()-ed"
+        @reexec_pid = 0
+        self.pid = @pid.chomp('.oldbin') if @pid
+        proc_name('master') if @worker_processes
+      else
+        worker = @workers.delete(wpid)
+        desc = worker ? "worker=#{worker.nr}" : "(unknown)"
+        m = "reaped #{status.inspect} #{desc}"
+        status.success? ? @logger.info(m) : @logger.error(m)
+      end
+    rescue Errno::ECHILD
+      return
+    end while true
+  end
 end