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
will be called with the name of the signal.
The name of the thread-local variable that stores pending signals.
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
Simulate the receipt of the specified signal
(probably only
useful in testing).
# File lib/cztop/reactor/signal_handling.rb, line 73
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.reset_signal_traps( *signals )
self.clean_up_signal_handling( reactor )
end
Tear down the data structures for signal handling
# File lib/cztop/reactor/signal_handling.rb, line 100
def clean_up_signal_handling( reactor )
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
Thread.main[ SIGNAL_QUEUE_KEY ].clear
end
Look for any signals that arrived and handle them.
# File lib/cztop/reactor/signal_handling.rb, line 113
def handle_queued_signals( _event )
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 122
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 146
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 156
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 134
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 84
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 128
def wake_up
@self_pipe[:writer].signal( 1 )
end