diff options
author | Eric Wong <e@80x24.org> | 2017-03-23 00:06:08 +0000 |
---|---|---|
committer | Eric Wong <e@80x24.org> | 2017-03-23 00:06:08 +0000 |
commit | 3acb0d5d2592415e8f415aea37c929a4392142fc (patch) | |
tree | df201e152873f9fb6e4435a3aca590de593ccde6 /lib/unicorn/http_server.rb | |
parent | 079da35629a507719a7c15324cfba4e9c5a7be4d (diff) | |
parent | d0afe48696a643a5d7400b3db1d68cfd81e8cd38 (diff) | |
download | unicorn-3acb0d5d2592415e8f415aea37c929a4392142fc.tar.gz |
* origin/worker_exec: Don't pass a block for fork when forking workers Add worker_exec configuration option
Diffstat (limited to 'lib/unicorn/http_server.rb')
-rw-r--r-- | lib/unicorn/http_server.rb | 77 |
1 files changed, 60 insertions, 17 deletions
diff --git a/lib/unicorn/http_server.rb b/lib/unicorn/http_server.rb index 4a2ccce..3827f2e 100644 --- a/lib/unicorn/http_server.rb +++ b/lib/unicorn/http_server.rb @@ -15,7 +15,7 @@ class Unicorn::HttpServer :before_fork, :after_fork, :before_exec, :listener_opts, :preload_app, :orig_app, :config, :ready_pipe, :user - attr_writer :after_worker_exit, :after_worker_ready + attr_writer :after_worker_exit, :after_worker_ready, :worker_exec attr_reader :pid, :logger include Unicorn::SocketHelper @@ -106,6 +106,14 @@ class Unicorn::HttpServer # list of signals we care about and trap in master. @queue_sigs = [ :WINCH, :QUIT, :INT, :TERM, :USR1, :USR2, :HUP, :TTIN, :TTOU ] + + @worker_data = if worker_data = ENV['UNICORN_WORKER'] + worker_data = worker_data.split(',').map!(&:to_i) + worker_data[1] = worker_data.slice!(1..2).map do |i| + Kgio::Pipe.for_fd(i) + end + worker_data + end end # Runs the thing. Returns self so you can run join on it @@ -114,7 +122,7 @@ class Unicorn::HttpServer # this pipe is used to wake us up from select(2) in #join when signals # are trapped. See trap_deferred. @self_pipe.replace(Unicorn.pipe) - @master_pid = $$ + @master_pid = @worker_data ? Process.ppid : $$ # setup signal handlers before writing pid file in case people get # trigger happy and send signals as soon as the pid file exists. @@ -431,11 +439,7 @@ class Unicorn::HttpServer end @reexec_pid = fork do - listener_fds = {} - LISTENERS.each do |sock| - sock.close_on_exec = false - listener_fds[sock.fileno] = sock - end + listener_fds = listener_sockets ENV['UNICORN_FD'] = listener_fds.keys.join(',') Dir.chdir(START_CTX[:cwd]) cmd = [ START_CTX[0] ].concat(START_CTX[:argv]) @@ -443,12 +447,7 @@ class Unicorn::HttpServer # avoid leaking FDs we don't know about, but let before_exec # unset FD_CLOEXEC, if anything else in the app eventually # relies on FD inheritence. - (3..1024).each do |io| - next if listener_fds.include?(io) - io = IO.for_fd(io) rescue next - io.autoclose = false - io.close_on_exec = true - end + close_sockets_on_exec(listener_fds) # exec(command, hash) works in at least 1.9.1+, but will only be # required in 1.9.4/2.0.0 at earliest. @@ -460,6 +459,40 @@ class Unicorn::HttpServer proc_name 'master (old)' end + def worker_spawn(worker) + listener_fds = listener_sockets + env = {} + env['UNICORN_FD'] = listener_fds.keys.join(',') + + listener_fds[worker.to_io.fileno] = worker.to_io + listener_fds[worker.master.fileno] = worker.master + + worker_info = [worker.nr, worker.to_io.fileno, worker.master.fileno] + env['UNICORN_WORKER'] = worker_info.join(',') + + close_sockets_on_exec(listener_fds) + + Process.spawn(env, START_CTX[0], *START_CTX[:argv], listener_fds) + end + + def listener_sockets + listener_fds = {} + LISTENERS.each do |sock| + sock.close_on_exec = false + listener_fds[sock.fileno] = sock + end + listener_fds + end + + def close_sockets_on_exec(sockets) + (3..1024).each do |io| + next if sockets.include?(io) + io = IO.for_fd(io) rescue next + io.autoclose = false + io.close_on_exec = true + end + end + # forcibly terminate all workers that haven't checked in in timeout seconds. The timeout is implemented using an unlinked File def murder_lazy_workers next_sleep = @timeout - 1 @@ -496,19 +529,29 @@ class Unicorn::HttpServer end def spawn_missing_workers + if @worker_data + worker = Unicorn::Worker.new(*@worker_data) + after_fork_internal + worker_loop(worker) + exit + end + worker_nr = -1 until (worker_nr += 1) == @worker_processes @workers.value?(worker_nr) and next worker = Unicorn::Worker.new(worker_nr) before_fork.call(self, worker) - if pid = fork - @workers[pid] = worker - worker.atfork_parent - else + + pid = @worker_exec ? worker_spawn(worker) : fork + + unless pid after_fork_internal worker_loop(worker) exit end + + @workers[pid] = worker + worker.atfork_parent end rescue => e @logger.error(e) rescue nil |