diff options
author | Eric Wong <normalperson@yhbt.net> | 2010-01-05 16:10:17 -0800 |
---|---|---|
committer | Eric Wong <normalperson@yhbt.net> | 2010-01-05 16:10:17 -0800 |
commit | 841f967666292f634c6af485f7ac452f36cc2028 (patch) | |
tree | 4dba86c2fb545a57c7728d1275d276e12d3b26d5 | |
parent | 40924ca5f42a5708159ac27b992805f24ecbae9b (diff) | |
parent | fe005f50efc8db5b9f4b2387b3b2c42f12d7c2c0 (diff) | |
download | unicorn-841f967666292f634c6af485f7ac452f36cc2028.tar.gz |
* ready_pipe: launcher: no point in sync-ing $stdin launcher: fix compatibility with other servers clarify errors when listeners fail to bind launcher: descriptive error message on startup failure Avoid leaking ready pipe file descriptor to workers exit with failure if master dies when daemonized
-rwxr-xr-x | bin/unicorn | 2 | ||||
-rwxr-xr-x | bin/unicorn_rails | 2 | ||||
-rw-r--r-- | lib/unicorn.rb | 14 | ||||
-rw-r--r-- | lib/unicorn/launcher.rb | 38 | ||||
-rw-r--r-- | test/exec/test_exec.rb | 2 |
5 files changed, 47 insertions, 11 deletions
diff --git a/bin/unicorn b/bin/unicorn index 651c2ff..5af021d 100755 --- a/bin/unicorn +++ b/bin/unicorn @@ -161,5 +161,5 @@ if $DEBUG }) end -Unicorn::Launcher.daemonize! if daemonize +Unicorn::Launcher.daemonize!(options) if daemonize Unicorn.run(app, options) diff --git a/bin/unicorn_rails b/bin/unicorn_rails index 4a22a8c..b1458fc 100755 --- a/bin/unicorn_rails +++ b/bin/unicorn_rails @@ -202,6 +202,6 @@ end if daemonize options[:pid] = rails_pid - Unicorn::Launcher.daemonize! + Unicorn::Launcher.daemonize!(options) end Unicorn.run(app, options) diff --git a/lib/unicorn.rb b/lib/unicorn.rb index 225e00a..69ecf33 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 = {} @@ -327,6 +328,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 @@ -531,7 +537,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/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/test/exec/test_exec.rb b/test/exec/test_exec.rb index fc0719b..24ba856 100644 --- a/test/exec/test_exec.rb +++ b/test/exec/test_exec.rb @@ -805,7 +805,7 @@ EOF exec($unicorn_bin, "-D", "-l#{@addr}:#{@port}", "-c#{ucfg.path}") end pid, status = Process.waitpid2(pid) - assert status.success?, "original process exited successfully" + assert ! status.success?, "original process exited successfully" sleep 1 # can't waitpid on a daemonized process :< assert err.stat.size > 0 end |