class ServerEngine::Supervisor

Attributes

server[R]

server is available after start_server() call.

Public Class Methods

create_server_proc(server_module, worker_module, config) click to toggle source
# File lib/serverengine/supervisor.rb, line 90
def self.create_server_proc(server_module, worker_module, config)
  wt = config[:worker_type] || 'embedded'
  case wt
  when 'embedded'
    server_class = EmbeddedServer
  when 'process'
    server_class = MultiProcessServer
  when 'thread'
    server_class = MultiThreadServer
  when 'spawn'
    server_class = MultiSpawnServer
  else
    raise ArgumentError, "unexpected :worker_type option #{wt}"
  end

  lambda {|load_config_proc,logger|
    s = server_class.new(worker_module, load_config_proc)
    s.logger = logger
    s.extend(ServerInitializer)
    s.extend(server_module) if server_module
    s.instance_eval { initialize }
    s
  }
end
new(server_module, worker_module, load_config_proc={}, &block) click to toggle source
Calls superclass method ServerEngine::ConfigLoader::new
# File lib/serverengine/supervisor.rb, line 34
def initialize(server_module, worker_module, load_config_proc={}, &block)
  @server_module = server_module
  @worker_module = worker_module

  @detach_flag = BlockingFlag.new
  @stop = false

  @pm = ProcessManager.new(
    auto_tick: false,
    graceful_kill_signal: Signals::GRACEFUL_STOP,
    immediate_kill_signal: Signals::IMMEDIATE_STOP,
    enable_heartbeat: true,
    auto_heartbeat: true,
  )

  super(load_config_proc, &block)

  @create_server_proc = Supervisor.create_server_proc(server_module, worker_module, @config)
  @server_process_name = @config[:server_process_name]

  @restart_server_process = !!@config[:restart_server_process]
  @unrecoverable_exit_codes = @config.fetch(:unrecoverable_exit_codes, [])
  @enable_detach = !!@config[:enable_detach]
  @exit_on_detach = !!@config[:exit_on_detach]
  @disable_reload = !!@config[:disable_reload]

  @command_pipe = @config.fetch(:command_pipe, nil)

  @command_sender = @config.fetch(:command_sender, ServerEngine.windows? ? "pipe" : "signal")
  if @command_sender == "pipe"
    extend CommandSender::Pipe
  else
    extend CommandSender::Signal
  end
end

Public Instance Methods

create_server(logger) click to toggle source
# File lib/serverengine/supervisor.rb, line 115
def create_server(logger)
  @server = @create_server_proc.call(@load_config_proc, logger)
end
detach(stop_graceful) click to toggle source
# File lib/serverengine/supervisor.rb, line 142
def detach(stop_graceful)
  if @enable_detach
    @detach_flag.set!
    _stop(stop_graceful)
  else
    stop(stop_graceful)
  end
end
dump() click to toggle source
# File lib/serverengine/supervisor.rb, line 151
def dump
  _dump
end
install_signal_handlers() click to toggle source
# File lib/serverengine/supervisor.rb, line 155
def install_signal_handlers
  s = self
  if @command_pipe
    Thread.new do
      until @command_pipe.closed?
        case @command_pipe.gets.chomp
        when "GRACEFUL_STOP"
          s.stop(true)
        when "IMMEDIATE_STOP"
          s.stop(false)
        when "GRACEFUL_RESTART"
          s.restart(true)
        when "IMMEDIATE_RESTART"
          s.restart(false)
        when "RELOAD"
          s.reload
        when "DETACH"
          s.detach(true)
        when "DUMP"
          s.dump
        end
      end
    end
  else
    SignalThread.new do |st|
      st.trap(Signals::GRACEFUL_STOP) { s.stop(true) }
      st.trap(Signals::IMMEDIATE_STOP) { s.stop(false) }
      st.trap(Signals::GRACEFUL_RESTART) { s.restart(true) }
      st.trap(Signals::IMMEDIATE_RESTART) { s.restart(false) }
      st.trap(Signals::RELOAD) { s.reload }
      st.trap(Signals::DETACH) { s.detach(true) }
      st.trap(Signals::DUMP) { s.dump }
    end
  end
end
logger=(logger) click to toggle source
Calls superclass method
# File lib/serverengine/supervisor.rb, line 224
def logger=(logger)
  super
  @pm.logger = @logger
end
main() click to toggle source
# File lib/serverengine/supervisor.rb, line 191
def main
  # just in case Supervisor is not created by Daemon
  create_logger unless @logger

  @pmon = start_server

  while true
    # keep the child process alive in this loop
    until @detach_flag.wait(0.5)
      if try_join
        return if @stop   # supervisor stoppped explicitly
        if @stop_status # set exit code told by server
          raise SystemExit.new(@stop_status)
        end

        # child process died unexpectedly.
        # sleep @server_detach_wait sec and reboot process
        @pmon = reboot_server
      end
    end

    wait_until = Time.now + @server_detach_wait
    while (w = wait_until - Time.now) > 0
      break if try_join
      sleep [0.5, w].min
    end

    return if @exit_on_detach

    @detach_flag.reset!
  end
end
reload() click to toggle source
# File lib/serverengine/supervisor.rb, line 134
def reload
  unless @disable_reload
    reload_config
  end
  @logger.reopen! if @logger
  _reload
end
reload_config() click to toggle source
# File lib/serverengine/supervisor.rb, line 73
def reload_config
  super

  @server_detach_wait = @config[:server_detach_wait] || 10.0
  @server_restart_wait = @config[:server_restart_wait] || 1.0

  @pm.configure(@config, prefix: 'server_')

  nil
end
restart(stop_graceful) click to toggle source
# File lib/serverengine/supervisor.rb, line 124
def restart(stop_graceful)
  reload_config
  @logger.reopen! if @logger
  if @restart_server_process
    _stop(stop_graceful)
  else
    _restart(stop_graceful)
  end
end
stop(stop_graceful) click to toggle source
# File lib/serverengine/supervisor.rb, line 119
def stop(stop_graceful)
  @stop = true
  _stop(stop_graceful)
end

Private Instance Methods

reboot_server() click to toggle source
# File lib/serverengine/supervisor.rb, line 298
def reboot_server
  # try reboot for ever until @detach_flag is set
  while true
    wait = @server_restart_wait - (Time.now - @last_start_time)
    if @detach_flag.wait(wait > 0 ? wait : 0.1)
      break
    end

    begin
      return start_server
    rescue
      ServerEngine.dump_uncaught_error($!)
    end
  end

  return nil
end
send_signal(sig) click to toggle source
# File lib/serverengine/supervisor.rb, line 231
def send_signal(sig)
  @pmon.send_signal(sig) if @pmon
  nil
end
start_server() click to toggle source
# File lib/serverengine/supervisor.rb, line 250
def start_server
  if @command_sender == "pipe"
    inpipe, @command_sender_pipe = IO.pipe
  end

  unless ServerEngine.windows?
    s = create_server(logger)
    @last_start_time = Time.now

    begin
      m = @pm.fork do
        $0 = @server_process_name if @server_process_name
        if @command_sender == "pipe"
          @command_sender_pipe.close
          s.instance_variable_set(:@command_pipe, inpipe)
        end
        s.install_signal_handlers

        begin
          s.main
        rescue SystemExit => e
          @logger.info "Server is exitting with code #{e.status}"
          exit! e.status
        end
      end
      if @command_sender == "pipe"
        inpipe.close
      end

      return m
    ensure
      s.after_start
    end
  else # if ServerEngine.windows?
    exconfig = {}
    if @command_sender == "pipe"
      exconfig[:in] = inpipe
    end
    @last_start_time = Time.now
    m = @pm.spawn(*Array(config[:windows_daemon_cmdline]), exconfig)
    if @command_sender == "pipe"
      inpipe.close
    end

    return m
  end
end
try_join() click to toggle source
# File lib/serverengine/supervisor.rb, line 236
def try_join
  if stat = @pmon.try_join
    @logger.info "Server finished#{@stop ? '' : ' unexpectedly'} with #{ServerEngine.format_join_status(stat)}"
    if !@stop && stat.is_a?(Process::Status) && stat.exited? && @unrecoverable_exit_codes.include?(stat.exitstatus)
      @stop_status = stat.exitstatus
    end
    @pmon = nil
    return stat
  else
    @pm.tick
    return false
  end
end