diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/unicorn.rb | 17 | ||||
-rw-r--r-- | lib/unicorn/const.rb | 2 | ||||
-rw-r--r-- | lib/unicorn/launcher.rb | 38 | ||||
-rw-r--r-- | lib/unicorn/tee_input.rb | 55 |
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 |