lib/main_loop/dispatcher.rb
require 'monitor'
require 'logger'
module MainLoop
class Dispatcher
include MonitorMixin
attr_reader :bus, :handlers, :logger
def initialize(bus, timeout: 5, logger: nil)
super()
@bus = bus
@timeout = timeout
@handlers = []
@logger = logger || Logger.new(nil)
@exit_code = 0
end
def reap(statuses)
statuses.each do |(pid, status)|
reap_by_id(pid, status)
end
end
def reap_by_id(id, status)
synchronize do
if (handler = handlers.find {|h| h.id == id })
logger.info("Reap handler #{handler.name.inspect}. Status: #{status.inspect}")
handler.reap(status)
else
logger.debug("Reap unknown handler. Status: #{status.inspect}. Skipped")
end
end
end
def add_handler(handler)
synchronize do
handler.term if terminating?
handlers << handler
end
end
def terminating?
@terminating_at
end
def term
synchronize do
if terminating?
logger.info('Terminate FORCE all handlers')
handlers.each(&:kill)
else
@terminating_at ||= Time.now
logger.info('Terminate all handlers')
handlers.each(&:term)
end
end
end
def crash
@exit_code = 3
term unless terminating?
end
def tick
log_status if logger.debug?
return unless terminating?
try_exit!
return if @killed || !need_force_kill?
@killed = true
logger.info('Killing all handlers by timeout')
handlers.each(&:kill)
end
def need_force_kill?
@terminating_at && (Time.now - @terminating_at) >= @timeout
end
def pids
handlers.map{|h| h.pid rescue nil }.compact
end
# :nocov:
def try_exit!
synchronize do
return unless handlers.all?(&:finished?)
logger.info('All handlers finished exiting...')
status = handlers.all?(&:success?) ? @exit_code : 1
logger.info("Exit: #{status}")
exit status
end
end
# :nocov:
# :nocov:
def log_status
total = handlers.size
running = handlers.count(&:running?)
finihsed = handlers.count(&:finished?)
term_text = terminating? ? 'TERM' : ''
logger.debug("Total:#{total} Running:#{running} Finihsed:#{finihsed}. #{term_text}".strip)
end
# :nocov:
end
end