about summary refs log tree commit homepage
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/unicorn.rb17
-rw-r--r--lib/unicorn/const.rb2
-rw-r--r--lib/unicorn/launcher.rb38
-rw-r--r--lib/unicorn/tee_input.rb55
4 files changed, 90 insertions, 22 deletions
diff --git a/lib/unicorn.rb b/lib/unicorn.rb
index cf58165..7a1ef34 100644
--- a/lib/unicorn.rb
+++ b/lib/unicorn.rb
@@ -38,7 +38,7 @@ module Unicorn
                                 :before_fork, :after_fork, :before_exec,
                                 :logger, :pid, :app, :preload_app,
                                 :reexec_pid, :orig_app, :init_listeners,
-                                :master_pid, :config)
+                                :master_pid, :config, :ready_pipe)
     include ::Unicorn::SocketHelper
 
     # prevents IO objects in here from being GC-ed
@@ -162,6 +162,7 @@ module Unicorn
     def initialize(app, options = {})
       self.app = app
       self.reexec_pid = 0
+      self.ready_pipe = options.delete(:ready_pipe)
       self.init_listeners = options[:listeners] ? options[:listeners].dup : []
       self.config = Configurator.new(options.merge(:use_defaults => true))
       self.listener_opts = {}
@@ -310,6 +311,9 @@ module Unicorn
                      "(#{tries < 0 ? 'infinite' : tries} tries left)"
         sleep(delay)
         retry
+      rescue => err
+        logger.fatal "error adding listener addr=#{address}"
+        raise err
       end
     end
 
@@ -328,6 +332,11 @@ module Unicorn
       trap(:CHLD) { |sig_nr| awaken_master }
       proc_name 'master'
       logger.info "master process ready" # test_exec.rb relies on this message
+      if ready_pipe
+        ready_pipe.syswrite($$.to_s)
+        ready_pipe.close rescue nil
+        self.ready_pipe = nil
+      end
       begin
         loop do
           reap_all_workers
@@ -532,7 +541,11 @@ module Unicorn
         WORKERS.values.include?(worker_nr) and next
         worker = Worker.new(worker_nr, Unicorn::Util.tmpio)
         before_fork.call(self, worker)
-        WORKERS[fork { worker_loop(worker) }] = worker
+        WORKERS[fork {
+          ready_pipe.close if ready_pipe
+          self.ready_pipe = nil
+          worker_loop(worker)
+        }] = worker
       end
     end
 
diff --git a/lib/unicorn/const.rb b/lib/unicorn/const.rb
index 81f61c8..f70502e 100644
--- a/lib/unicorn/const.rb
+++ b/lib/unicorn/const.rb
@@ -7,7 +7,7 @@ module Unicorn
   # gave about a 3% to 10% performance improvement over using the strings directly.
   # Symbols did not really improve things much compared to constants.
   module Const
-    UNICORN_VERSION="0.95.2"
+    UNICORN_VERSION="0.95.3"
 
     DEFAULT_HOST = "0.0.0.0" # default TCP listen host address
     DEFAULT_PORT = 8080      # default TCP listen port
diff --git a/lib/unicorn/launcher.rb b/lib/unicorn/launcher.rb
index 1229b84..e71f93b 100644
--- a/lib/unicorn/launcher.rb
+++ b/lib/unicorn/launcher.rb
@@ -1,6 +1,6 @@
 # -*- encoding: binary -*-
 
-$stdin.sync = $stdout.sync = $stderr.sync = true
+$stdout.sync = $stderr.sync = true
 $stdin.binmode
 $stdout.binmode
 $stderr.binmode
@@ -19,21 +19,47 @@ class Unicorn::Launcher
   #     the directory it was started in when being re-executed
   #     to pickup code changes if the original deployment directory
   #     is a symlink or otherwise got replaced.
-  def self.daemonize!
+  def self.daemonize!(options = nil)
     $stdin.reopen("/dev/null")
 
     # We only start a new process group if we're not being reexecuted
     # and inheriting file descriptors from our parent
     unless ENV['UNICORN_FD']
-      exit if fork
-      Process.setsid
-      exit if fork
+      if options
+        # grandparent - reads pipe, exits when master is ready
+        #  \_ parent  - exits immediately ASAP
+        #      \_ unicorn master - writes to pipe when ready
 
+        rd, wr = IO.pipe
+        grandparent = $$
+        if fork
+          wr.close # grandparent does not write
+        else
+          rd.close # unicorn master does not read
+          Process.setsid
+          exit if fork # parent dies now
+        end
+
+        if grandparent == $$
+          # this will block until HttpServer#join runs (or it dies)
+          master_pid = (rd.readpartial(16) rescue nil).to_i
+          unless master_pid > 1
+            warn "master failed to start, check stderr log for details"
+            exit!(1)
+          end
+          exit 0
+        else # unicorn master process
+          options[:ready_pipe] = wr
+        end
+      else # backwards compat
+        exit if fork
+        Process.setsid
+        exit if fork
+      end
       # $stderr/$stderr can/will be redirected separately in the Unicorn config
       Unicorn::Configurator::DEFAULTS[:stderr_path] = "/dev/null"
       Unicorn::Configurator::DEFAULTS[:stdout_path] = "/dev/null"
     end
-    $stdin.sync = $stdout.sync = $stderr.sync = true
   end
 
 end
diff --git a/lib/unicorn/tee_input.rb b/lib/unicorn/tee_input.rb
index 0669b48..bb86c40 100644
--- a/lib/unicorn/tee_input.rb
+++ b/lib/unicorn/tee_input.rb
@@ -3,15 +3,20 @@
 module Unicorn
 
   # acts like tee(1) on an input input to provide a input-like stream
-  # while providing rewindable semantics through a File/StringIO
-  # backing store.  On the first pass, the input is only read on demand
-  # so your Rack application can use input notification (upload progress
-  # and like).  This should fully conform to the Rack::InputWrapper
+  # while providing rewindable semantics through a File/StringIO backing
+  # store.  On the first pass, the input is only read on demand so your
+  # Rack application can use input notification (upload progress and
+  # like).  This should fully conform to the Rack::Lint::InputWrapper
   # specification on the public API.  This class is intended to be a
-  # strict interpretation of Rack::InputWrapper functionality and will
-  # not support any deviations from it.
+  # strict interpretation of Rack::Lint::InputWrapper functionality and
+  # will not support any deviations from it.
+  #
+  # When processing uploads, Unicorn exposes a TeeInput object under
+  # "rack.input" of the Rack environment.
   class TeeInput < Struct.new(:socket, :req, :parser, :buf)
 
+    # Initializes a new TeeInput object.  You normally do not have to call
+    # this unless you are writing an HTTP server.
     def initialize(*args)
       super(*args)
       @size = parser.content_length
@@ -24,10 +29,16 @@ module Unicorn
       end
     end
 
-    # returns the size of the input.  This is what the Content-Length
-    # header value should be, and how large our input is expected to be.
-    # For TE:chunked, this requires consuming all of the input stream
-    # before returning since there's no other way
+    # :call-seq:
+    #   ios.size  => Integer
+    #
+    # Returns the size of the input.  For requests with a Content-Length
+    # header value, this will not read data off the socket and just return
+    # the value of the Content-Length header as an Integer.
+    #
+    # For Transfer-Encoding:chunked requests, this requires consuming
+    # all of the input stream before returning since there's no other
+    # way to determine the size of the request body beforehand.
     def size
       @size and return @size
 
@@ -41,8 +52,7 @@ module Unicorn
       @size = @tmp.size
     end
 
-    # call-seq:
-    #   ios = env['rack.input']
+    # :call-seq:
     #   ios.read([length [, buffer ]]) => string, buffer, or nil
     #
     # Reads at most length bytes from the I/O stream, or to the end of
@@ -82,7 +92,15 @@ module Unicorn
       end
     end
 
-    # takes zero arguments for strict Rack::Lint compatibility, unlike IO#gets
+    # :call-seq:
+    #   ios.gets   => string or nil
+    #
+    # Reads the next ``line'' from the I/O stream; lines are separated
+    # by the global record separator ($/, typically "\n"). A global
+    # record separator of nil reads the entire unread contents of ios.
+    # Returns nil if called at the end of file.
+    # This takes zero arguments for strict Rack::Lint compatibility,
+    # unlike IO#gets.
     def gets
       socket or return @tmp.gets
       nil == $/ and return read
@@ -109,6 +127,11 @@ module Unicorn
       line
     end
 
+    # :call-seq:
+    #   ios.each { |line| block }  => ios
+    #
+    # Executes the block for every ``line'' in *ios*, where lines are
+    # separated by the global record separator ($/, typically "\n").
     def each(&block)
       while line = gets
         yield line
@@ -117,6 +140,12 @@ module Unicorn
       self # Rack does not specify what the return value is here
     end
 
+    # :call-seq:
+    #   ios.rewind    => 0
+    #
+    # Positions the *ios* pointer to the beginning of input, returns
+    # the offset (zero) of the +ios+ pointer.  Subsequent reads will
+    # start from the beginning of the previously-buffered input.
     def rewind
       @tmp.rewind # Rack does not specify what the return value is here
     end