class Strelka::MultiRunner

Load multiple simultaneous Strelka handlers (of a single type) with proper signal handling.

Constants

QUEUE_SIGS

Signals we understand.

Attributes

app_class[R]

The class name of the handler.

handler_pids[R]

The child handler pids.

number[R]

How many handler children to manage.

running[R]

In this instance currently managing children?

Public Class Methods

new( app_class, number=2 ) click to toggle source

Create a new multirunner instance given a handler app_class, and the number of instances to start.

# File lib/strelka/multirunner.rb, line 24
def initialize( app_class, number=2 )
        @handler_pids = []
        @running      = false
        @app_class    = app_class
        @number       = number

        self.set_up_signal_handling
end

Public Instance Methods

run() click to toggle source

Start the child handlers via fork(), block for signals.

# File lib/strelka/multirunner.rb, line 47
def run
        @running = true

        # Set up traps for common signals
        self.set_signal_traps( *QUEUE_SIGS )

        self.log.debug "Starting multirunner loop..."
        self.spawn_children
        while self.running
                self.reap_children if self.wait_for_signals
        end
        self.log.debug "Ending multirunner."

        # Restore the default signal handlers
        self.reset_signal_traps( *QUEUE_SIGS )

        return
end

Protected Instance Methods

handle_signal( sig ) click to toggle source

Handle signals.

# File lib/strelka/multirunner.rb, line 120
def handle_signal( sig )
        self.log.debug "Handling signal %s in PID %d" % [ sig, Process.pid ]
        case sig
        when :INT, :TERM, :QUIT
                if @running
                        self.log.warn "%s signal: graceful shutdown" % [ sig ]
                        self.kill_children( sig )
                        @running = false
                else
                        self.ignore_signals
                        self.log.warn "%s signal: forceful shutdown" % [ sig ]
                        self.kill_children( :KILL )
                        exit!( 255 )
                end

        when :CHLD
                self.log.info "Got SIGCHLD."
                # Just need to wake up, nothing else necessary

        else
                self.log.warn "Unhandled signal %s" % [ sig ]
        end
end
kill_children( signal=:TERM ) click to toggle source

Kill all current children with the specified signal. Returns true if the signal was sent to one or more children.

# File lib/strelka/multirunner.rb, line 99
def kill_children( signal=:TERM )
        return false if self.handler_pids.empty?

        self.log.info "Sending %s signal to %d task pids: %p." %
                 [ signal, self.handler_pids.length, self.handler_pids ]
        self.handler_pids.each do |pid|
                begin
                        Process.kill( signal, pid )
                rescue Errno::ESRCH => err
                        self.log.error "%p when trying to %s child %d: %s" %
                                [ err.class, signal, pid, err.message ]
                end
        end

        return true
rescue Errno::ESRCH
        self.log.debug "Ignoring signals to unreaped children."
end
reap_children() click to toggle source

Clean up after any children that have died.

# File lib/strelka/multirunner.rb, line 86
def reap_children
        pid, status = Process.waitpid2( -1, Process::WNOHANG|Process::WUNTRACED )
        self.log.debug "  waitpid2 returned: [ %p, %p ]" % [ pid, status ]
        while pid
                self.handler_pids.delete( pid )
                pid, status = Process.waitpid2( -1, Process::WNOHANG|Process::WUNTRACED )
                self.log.debug "  waitpid2 returned: [ %p, %p ]" % [ pid, status ]
        end
end
spawn_children() click to toggle source

Start the handlers using fork().

# File lib/strelka/multirunner.rb, line 72
def spawn_children
        self.number.times do
                Strelka.call_before_fork_hooks
                pid = Process.fork do
                        Process.setpgrp
                        self.app_class.run
                end
                self.handler_pids << pid
                Process.setpgid( pid, 0 )
        end
end