in lib/puma/cluster.rb [401:533]
def run
@status = :run
output_header "cluster"
log "* Process workers: #{@options[:workers]}"
before = Thread.list
if preload?
log "* Preloading application"
load_and_bind
after = Thread.list
if after.size > before.size
threads = (after - before)
if threads.first.respond_to? :backtrace
log "! WARNING: Detected #{after.size-before.size} Thread(s) started in app boot:"
threads.each do |t|
log "! #{t.inspect} - #{t.backtrace ? t.backtrace.first : ''}"
end
else
log "! WARNING: Detected #{after.size-before.size} Thread(s) started in app boot"
end
end
else
log "* Phased restart available"
unless @launcher.config.app_configured?
error "No application configured, nothing to run"
exit 1
end
@launcher.binder.parse @options[:binds], self
end
read, @wakeup = Puma::Util.pipe
setup_signals
@check_pipe, @suicide_pipe = Puma::Util.pipe
if daemon?
log "* Daemonizing..."
Process.daemon(true)
else
log "Use Ctrl-C to stop"
end
redirect_io
Plugins.fire_background
@launcher.write_state
start_control
@master_read, @worker_write = read, @wakeup
@launcher.config.run_hooks :before_fork, nil
spawn_workers
Signal.trap "SIGINT" do
stop
end
@launcher.events.fire_on_booted!
begin
force_check = false
while @status == :run
begin
if @phased_restart
start_phased_restart
@phased_restart = false
end
check_workers force_check
force_check = false
res = IO.select([read], nil, nil, Const::WORKER_CHECK_INTERVAL)
if res
req = read.read_nonblock(1)
next if !req || req == "!"
result = read.gets
pid = result.to_i
if w = @workers.find { |x| x.pid == pid }
case req
when "b"
w.boot!
log "- Worker #{w.index} (pid: #{pid}) booted, phase: #{w.phase}"
force_check = true
when "e"
w.instance_variable_set :@term, true
when "t"
w.term unless w.term?
force_check = true
when "p"
w.ping!(result.sub(/^\d+/,'').chomp)
end
else
log "! Out-of-sync worker list, no #{pid} worker"
end
end
rescue Interrupt
@status = :stop
end
end
stop_workers unless @status == :halt
ensure
@check_pipe.close
@suicide_pipe.close
read.close
@wakeup.close
end
end