class Puma::Cluster

def run

def run
  @status = :run
  output_header "cluster"
  # This is aligned with the output from Runner, see Runner#output_header
  log "*      Workers: #{@options[:workers]}"
  if preload?
    # Threads explicitly marked as fork safe will be ignored. Used in Rails,
    # but may be used by anyone. Note that we need to explicit
    # Process::Waiter check here because there's a bug in Ruby 2.6 and below
    # where calling thread_variable_get on a Process::Waiter will segfault.
    # We can drop that clause once those versions of Ruby are no longer
    # supported.
    fork_safe = ->(t) { !t.is_a?(Process::Waiter) && t.thread_variable_get(:fork_safe) }
    before = Thread.list.reject(&fork_safe)
    log "*     Restarts: (\u2714) hot (\u2716) phased"
    log "* Preloading application"
    load_and_bind
    after = Thread.list.reject(&fork_safe)
    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 "*     Restarts: (\u2714) hot (\u2714) phased"
    unless @config.app_configured?
      error "No application configured, nothing to run"
      exit 1
    end
    @launcher.binder.parse @options[:binds]
  end
  read, @wakeup = Puma::Util.pipe
  setup_signals
  # Used by the workers to detect if the master process dies.
  # If select says that @check_pipe is ready, it's because the
  # master has exited and @suicide_pipe has been automatically
  # closed.
  #
  @check_pipe, @suicide_pipe = Puma::Util.pipe
  # Separate pipe used by worker 0 to receive commands to
  # fork new worker processes.
  @fork_pipe, @fork_writer = Puma::Util.pipe
  log "Use Ctrl-C to stop"
  single_worker_warning
  redirect_io
  Plugins.fire_background
  @launcher.write_state
  start_control
  @master_read, @worker_write = read, @wakeup
  @config.run_hooks(:before_fork, nil, @log_writer)
  spawn_workers
  Signal.trap "SIGINT" do
    stop
  end
  begin
    booted = false
    in_phased_restart = false
    workers_not_booted = @options[:workers]
    while @status == :run
      begin
        if @phased_restart
          start_phased_restart
          @phased_restart = false
          in_phased_restart = true
          workers_not_booted = @options[:workers]
        end
        check_workers
        if read.wait_readable([0, @next_check - Time.now].max)
          req = read.read_nonblock(1)
          @next_check = Time.now if req == "!"
          next if !req || req == "!"
          result = read.gets
          pid = result.to_i
          if req == "b" || req == "f"
            pid, idx = result.split(':').map(&:to_i)
            w = @workers.find {|x| x.index == idx}
            w.pid = pid if w.pid.nil?
          end
          if w = @workers.find { |x| x.pid == pid }
            case req
            when "b"
              w.boot!
              log "- Worker #{w.index} (PID: #{pid}) booted in #{w.uptime.round(2)}s, phase: #{w.phase}"
              @next_check = Time.now
              workers_not_booted -= 1
            when "e"
              # external term, see worker method, Signal.trap "SIGTERM"
              w.term!
            when "t"
              w.term unless w.term?
            when "p"
              w.ping!(result.sub(/^\d+/,'').chomp)
              @events.fire(:ping!, w)
              if !booted && @workers.none? {|worker| worker.last_status.empty?}
                @events.fire_on_booted!
                debug_loaded_extensions("Loaded Extensions - master:") if @log_writer.debug?
                booted = true
              end
            end
          else
            log "! Out-of-sync worker list, no #{pid} worker"
          end
        end
        if in_phased_restart && workers_not_booted.zero?
          @events.fire_on_booted!
          debug_loaded_extensions("Loaded Extensions - master:") if @log_writer.debug?
          in_phased_restart = false
        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