module Daemonizer class Worker attr_reader :name attr_reader :pid def initialize(name, pm, engine, worker_id, &blk) raise ArgumentError, "Need a worker block!" unless block_given? @name = name @pm = pm @engine = engine.to_s @worker_block = blk @worker_id = worker_id end def logger @pm.logger end def shutdown? @pm.shutdown? end def run return if shutdown? if @engine == 'fork' @pid = Kernel.fork do @pid = Process.pid GDC.set "#{@pid}/#{@worker_id}" normal_exit = false begin $0 = "#{@name} worker: instance #{@worker_id}\0" @worker_block.call(@worker_id) normal_exit = true exit(0) rescue Exception => e message = SystemExit === e ? "exit(#{e.status})" : e.to_s if SystemExit === e and e.success? if normal_exit logger.info("Worker finished: normal return") else logger.error("Worker exited: #{message} at #{e.backtrace.first}") end else logger.error("Worker exited with error: #{message}\n #{e.backtrace.join("\n ")}") end logger.debug("Terminating #{@name} worker: #{@pid}") end end elsif @engine == 'thread' @thread = Thread.start do @worker_block.call end else raise ArgumentError, "Invalid engine name: #{@engine}" end rescue Exception => e logger.error("Exception from worker: #{e} at #{e.backtrace.first}") end def running?(verbose = false) if @engine == 'fork' return false unless @pid begin Process.waitpid(@pid, Process::WNOHANG) res = Process.kill(0, @pid) logger.debug("KILL(#{@pid}) = #{res}") if verbose return true rescue Errno::ESRCH, Errno::ECHILD, Errno::EPERM => e logger.error("Exception from kill: #{e} at #{e.backtrace.first}") if verbose return false end elsif @engine == 'thread' @thread && @thread.alive? else raise ArgumentError, "Invalid engine name: #{@engine}" end end def stop(force = false) if @engine == 'fork' begin sig = force ? 'SIGKILL' : 'SIGTERM' logger.debug("Sending #{sig} to ##{@pid}") Process.kill(sig, @pid) rescue Errno::ESRCH, Errno::ECHILD, Errno::EPERM=> e logger.error("Exception from kill: #{e} at #{e.backtrace.first}") end elsif @engine == 'thread' force && !defined?(::JRuby) ? @thread.kill! : @thread.kill else raise ArgumentError, "Invalid engine name: #{@engine}" end end end end