module CZTop::Reactor::SignalHandling
A mixin that adds methods for running a process with queued signal handling via a ZMQ PAIR socket.
require 'cztop' require 'cztop/reactor' require 'cztop/reactor/signal_handling' class MyDaemon include CZTop::Reactor::SignalHandling def start @reactor = CZTop::Reactor.new @reactor.register( @socket, :read, &self.method(:handle_io_event) ) self.with_signal_handler( @reactor, :HUP, :INT, :TERM ) do @reactor.start_polling( ignore_interrupts: true ) end end def stop @reactor.stop_polling end def handle_signal( signal_name ) case signal_name when :INT, :TERM, :HUP self.stop else super end end end
With this mixin included, you can wrap a block with a call to with_signal_handler
, and when a signal arrives, the handle_signal
method will be called with the name of the signal.
Constants
- SIGNAL_QUEUE_KEY
The name of the thread-local variable that stores pending signals.
Public Class Methods
Inclusion callback – add Loggability to including classes.
# File lib/cztop/reactor/signal_handling.rb, line 51 def self::included( mod ) super mod.extend( Loggability ) mod.log_to( :cztop ) unless Loggability.log_host?( mod ) end
Public Instance Methods
Simulate the receipt of the specified signal
(probably only useful in testing).
# File lib/cztop/reactor/signal_handling.rb, line 75 def simulate_signal( signal ) Thread.main[ SIGNAL_QUEUE_KEY ] << signal.to_sym self.wake_up end
Wrap a block in signal-handling.
# File lib/cztop/reactor/signal_handling.rb, line 59 def with_signal_handler( reactor, *signals ) self.set_up_signal_handling( reactor ) self.set_signal_traps( *signals ) return yield ensure self.log.debug "Going to reset signal traps..." self.reset_signal_traps( *signals ) self.log.debug "Going to clean up signal handler..." self.clean_up_signal_handling( reactor ) end
Protected Instance Methods
Tear down the data structures for signal handling
# File lib/cztop/reactor/signal_handling.rb, line 102 def clean_up_signal_handling( reactor ) if @self_pipe self.log.info "Cleaning up signal handler self-pipe." reactor.unregister( @self_pipe[:reader] ) @self_pipe[:writer].options.linger = 0 @self_pipe[:writer].close @self_pipe[:reader].options.linger = 0 @self_pipe[:reader].close else self.log.info "No self-pipe; skipping signal-handler cleanup." end Thread.main[ SIGNAL_QUEUE_KEY ].clear end
Look for any signals that arrived and handle them.
# File lib/cztop/reactor/signal_handling.rb, line 120 def handle_queued_signals( event ) event.socket.wait while sig = Thread.main[ SIGNAL_QUEUE_KEY ].shift self.log.debug " got a queued signal: %p" % [ sig ] self.handle_signal( sig ) end end
Default signal-handler callback – this raises an exception by default.
# File lib/cztop/reactor/signal_handling.rb, line 130 def handle_signal( signal_name ) raise NotImplementedError, "unhandled signal %s" % [ signal_name ] end
Set the traps for the specified signals
to IGNORE.
# File lib/cztop/reactor/signal_handling.rb, line 154 def ignore_signals( *signals ) self.log.debug "Ignoring signals." signals.each do |sig| next if sig == :CHLD Signal.trap( sig, :IGNORE ) end end
Set the traps for the specified signals
to the default handler.
# File lib/cztop/reactor/signal_handling.rb, line 164 def reset_signal_traps( *signals ) self.log.debug "Restoring default signal handlers." signals.each do |sig| Signal.trap( sig, :DEFAULT ) end end
Set up signal traps for the specified signals
.
# File lib/cztop/reactor/signal_handling.rb, line 142 def set_signal_traps( *signals ) self.log.debug "Setting up deferred signal handlers for signals: %p." % [ signals ] signals.each do |sig| Signal.trap( sig ) do Thread.main[ SIGNAL_QUEUE_KEY ] << sig self.wake_up end end end
Set up data structures for signal handling.
# File lib/cztop/reactor/signal_handling.rb, line 86 def set_up_signal_handling( reactor ) Thread.main[ SIGNAL_QUEUE_KEY ] = [] endpoint = "inproc://signal-handler-%s" % [ SecureRandom.hex(8) ] @self_pipe = { reader: CZTop::Socket::PAIR.new( "@#{endpoint}" ), writer: CZTop::Socket::PAIR.new( ">#{endpoint}" ) } # :TODO: Consider calling #set_unbounded on the PAIR sockets reactor.register( @self_pipe[:reader], :read, &self.method(:handle_queued_signals) ) end
Signal through the self-pipe that one or more signals has been queued.
# File lib/cztop/reactor/signal_handling.rb, line 136 def wake_up @self_pipe[:writer].signal( 1 ) end