require 'inbox-sync/notice/run_sync_error' module InboxSync class Runner attr_reader :syncs, :interval, :logger def initialize(*args) opts, syncs = [ args.last.kind_of?(Hash) ? args.pop : {}, args.flatten ] @syncs = syncs || [] @interval = opts[:interval].kind_of?(Fixnum) ? opts[:interval] : -1 @logger = opts[:logger] || Logger.new(STDOUT) @shutdown = false @running_syncs_thread = nil @sync_groups = [] Signal.trap('SIGINT', lambda{ raise Interrupt, 'SIGINT' }) Signal.trap('SIGQUIT', lambda{ raise Interrupt, 'SIGQUIT' }) Signal.trap('TERM', lambda{ raise Interrupt, 'TERM' }) end def start startup! loop do loop_run break if shutdown? end shutdown! end def stop if @shutdown != true main_log "Stop signal - waiting for any running syncs to finish." @shutdown = true end end def shutdown? !!@shutdown end protected def startup! main_log "Starting up the runner." if @interval < 0 main_log "run-once interval - signaling stop" stop @interval = 0 end end def shutdown! main_log "Shutting down the runner" end def loop_run begin main_log "Starting syncs in fresh thread." @running_syncs_thread = Thread.new { run_syncs_thread } if @interval > 0 main_log "Sleeping for #{@interval} second interval." sleep(@interval) main_log "Woke from sleep" end rescue Interrupt => err stop ensure join_syncs_thread end end def join_syncs_thread begin if @running_syncs_thread main_log "Waiting for running syncs thread to join..." @running_syncs_thread.join main_log "... running syncs thread has joined." end @running_syncs_thread = nil rescue Interrupt => err stop join_syncs_thread end end def run_syncs_thread thread_log "starting syncs..." begin @syncs.each do |sync| run_threaded_sync(sync) if !self.shutdown? end GC.start rescue Exception => err thread_log_error(err, :error) end thread_log "...syncs finished" end def run_threaded_sync(sync) begin sync.setup @sync_groups = sync.mail_item_groups thread_log "starting #{@sync_groups.size} sync threads..." @sync_groups.each do |group| thread_log "starting #{group.id} sync thread", :debug group.run(self) sleep(1) end rescue Exception => err run_sync_handle_error(sync, err) end begin @sync_groups.each do |group| thread_log "waiting for #{group.id} sync thread to join...", :debug group.join end thread_log "... all #{@sync_groups.size} sync threads are finished" sync.teardown rescue Exception => err run_sync_handle_error(sync, err) end end def run_sync_handle_error(sync, err) thread_log_error(err) notice = Notice::RunSyncError.new(sync.notify_smtp, sync.config.notify, { :error => err, :sync => sync }) sync.notify(notice) end def thread_log_error(err, level=:warn) thread_log "#{err.message} (#{err.class.name})", level err.backtrace.each { |bt| thread_log bt.to_s, level } end def main_log(msg, level=:info) log "[MAIN]: #{msg}", level end def thread_log(msg, level=:info) log "[THREAD]: #{msg}", level end def log(msg, level) @logger.send(level, msg) end end end