class Trident::SignalHandler
Constants
- CHUNK_SIZE
- MSG_STOP
- SIGNAL_QUEUE_MAX_SIZE
Attributes
instance[RW]
original_signal_handlers[R]
self_pipe[R]
signal_mappings[R]
signal_queue[R]
target[R]
Public Class Methods
join()
click to toggle source
# File lib/trident/signal_handler.rb, line 29 def join raise "No signal handler started" unless instance logger.info "Joining on signal handler" instance.join end
new(signal_mappings, target)
click to toggle source
# File lib/trident/signal_handler.rb, line 45 def initialize(signal_mappings, target) @target = target @signal_queue = [] @self_pipe = [] @original_signal_handlers = {} @main_loop = nil self.signal_mappings = signal_mappings end
reset_for_fork()
click to toggle source
# File lib/trident/signal_handler.rb, line 35 def reset_for_fork raise "No signal handler started" unless instance instance.reset_for_fork self.instance = nil end
start(signal_mappings, target)
click to toggle source
# File lib/trident/signal_handler.rb, line 15 def start(signal_mappings, target) raise "Already started, call stop if restart needed" if instance logger.info "Starting signal handler" self.instance = new(signal_mappings, target) instance.start end
stop()
click to toggle source
# File lib/trident/signal_handler.rb, line 22 def stop raise "No signal handler started" unless instance logger.info "Stopping signal handler" instance.stop self.instance = nil end
Public Instance Methods
join()
click to toggle source
# File lib/trident/signal_handler.rb, line 79 def join @main_loop.join end
reset_for_fork()
click to toggle source
# File lib/trident/signal_handler.rb, line 83 def reset_for_fork @self_pipe = [] reset_signal_handlers end
snooze()
click to toggle source
# File lib/trident/signal_handler.rb, line 99 def snooze msg = "" begin ready = IO.select([self_pipe.first], nil, nil, 1) or return ready.first && ready.first.first or return loop { msg << self_pipe.first.read_nonblock(CHUNK_SIZE) } rescue Errno::EAGAIN, Errno::EINTR end msg end
start()
click to toggle source
# File lib/trident/signal_handler.rb, line 54 def start setup_self_pipe setup_signal_handlers target.start if target.respond_to?(:start) @main_loop = Thread.new do logger.info "Main loop started" loop do signal_result = handle_signal_queue target.update if target.respond_to?(:update) break if signal_result == :break logger.debug "Snoozing main loop" msg = snooze if signal_queue.empty? logger.debug "Main loop awakened: #{msg.inspect}" break if msg == MSG_STOP end logger.info "Main loop exited" end end
stop()
click to toggle source
# File lib/trident/signal_handler.rb, line 74 def stop reset_signal_handlers wakeup(MSG_STOP) end
wakeup(msg='.')
click to toggle source
# File lib/trident/signal_handler.rb, line 88 def wakeup(msg='.') begin # mutexes (and thus logging) not allowed within a trap context # puts "Waking main loop" self_pipe.last.write_nonblock(msg) # wakeup master process from select rescue Errno::EAGAIN, Errno::EINTR # pipe is full, master should wake up anyways retry end end
Private Instance Methods
handle_signal_queue()
click to toggle source
# File lib/trident/signal_handler.rb, line 169 def handle_signal_queue signal_result = nil signal = signal_queue.shift if signal logger.info "Handling signal: #{signal}" actions = signal_mappings[signal] if actions actions.each do |action| logger.info "Sending to target: #{action}" signal_result = target.send(action) end end end signal_result end
reset_signal_handlers()
click to toggle source
# File lib/trident/signal_handler.rb, line 142 def reset_signal_handlers original_signal_handlers.each do |signal_name, original_handler| if signal_name == "SIGCHLD" && original_handler == nil # SIGCHLD handler needs to be default for stuff like # Process.detach to work correctly trap(signal_name, "DEFAULT") else trap(signal_name, original_handler) end end original_signal_handlers.clear end
setup_self_pipe()
click to toggle source
# File lib/trident/signal_handler.rb, line 125 def setup_self_pipe self_pipe.each { |io| io.close rescue nil } self_pipe.replace(IO.pipe) self_pipe.each { |io| io.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC) } end
setup_signal_handlers()
click to toggle source
# File lib/trident/signal_handler.rb, line 131 def setup_signal_handlers logger.info "Installing signal handlers" signal_mappings.each do |signal_name, actions| raise ArgumentError, "Target does not respond to action: #{actions}" unless actions.all? { |a| target.respond_to?(a) } logger.info "Adding signal mapping: #{signal_name} -> #{actions.inspect}" original_signal_handlers[signal_name] = trap_deferred(signal_name) end end
signal_mappings=(mappings)
click to toggle source
# File lib/trident/signal_handler.rb, line 112 def signal_mappings=(mappings) @signal_mappings = {} mappings.each do |k, v| k = "SIG#{k}" unless k =~ /^SIG/i k = k.upcase raise ArgumentError, "Duplicate signal handler: #{k}" if @signal_mappings.has_key?(k) @signal_mappings[k] = Array(v) end end
trap_deferred(signal)
click to toggle source
defer a signal for later processing in join
(master process)
# File lib/trident/signal_handler.rb, line 156 def trap_deferred(signal) trap(signal) do |signal_number| if signal_queue.size < SIGNAL_QUEUE_MAX_SIZE # mutexes (and thus logging) not allowed within a trap context # puts "Adding signal to queue: #{signal}" signal_queue << signal wakeup else $stderr.puts "Signal queue exceeded max size, ignoring #{signal}" end end end