lib/rbmk/server.rb in rbmk-0.1.0.c vs lib/rbmk/server.rb in rbmk-0.1.0.d

- old
+ new

@@ -1,32 +1,38 @@ require 'timeout' require 'rbmk/peer' -require 'rbmk/signal' module RBMK class Server + %w( CHLD INT HUP QUIT TERM ).each { |sig| const_set ('SIG%s' % sig).to_sym, Signal.list[sig] } + class Reaped < StandardError; end def initialize $master = true @arvg0 = File.basename Process.argv0 @workers = {} end def start + require 'rbmk/version' + $log.info sprintf('rbmk version %s (codename %p) is warming up in an orange glow', VERSION, CODENAME) require 'socket' @upstream = self.class.upstream $log.debug sprintf('Listening on %s:%s', self.class.host, self.class.port) @socket = TCPServer.new self.class.host, self.class.port - Signal.constants.each { |sig| Signal.trap Signal.const_get(sig), method(:trap) } $0 = sprintf '%s master at %s:%s', @arvg0, self.class.host, self.class.port + Signal.trap('CHLD') { raise SignalException, 'CHLD' } loop { accept } ensure @socket.close rescue nil - @workers.each { |pid| Process.kill 'TERM', pid rescue nil } - Process.waitall rescue nil - $log.debug 'Exiting' + $log.debug sprintf('Disposing of workers: %p', @workers.keys) + Signal.trap('CHLD') {} # we'll bury them in synchronous fashion + Thread.abort_on_exception = true + @workers.each { |pid,_| Thread.new { kill pid } } + sleep(0.1) while Thread.list.count > 1 # make sure everyone is dead + $log.info 'Shutdown sequence complete' end protected def self.host; '127.0.0.1' end @@ -37,50 +43,69 @@ require 'rbmk/upstream' RBMK::Upstream.new end def accept - peer = Peer.new(client = @socket.accept) + peer = Peer.new @socket.accept $log.info 'Connection from %s' % peer if pid = fork then - client.close - @workers[pid] = peer + peer.close + @workers[pid] = true else $log.debug 'Worker started' - act_as_a_child_for client, peer + @socket.close + act_as_a_child_for peer end - rescue Reaped - $log.debug $!.message - end - - def trap sig = nil - case sig - when nil then raise 'Something went wrong, trapped a nil.' - when Signal::CHLD then - pid, status = Process.wait2 -1, Process::WNOHANG - @workers.delete pid - raise Reaped.new('Reaped %s' % pid) - else raise 'Terminated on SIG%s' % Signal.signame(sig) + rescue SignalException + $log.debug 'Trapped %p' % $!.signm + case $!.signo + when SIGCHLD then reap + when SIGINT, SIGHUP, SIGTERM then exit + when SIGQUIT then + $log.debug 'Committing emergency suicide' + exit! + else raise $! end - rescue Errno::ECHILD - # okay, nothing to do end - def act_as_a_child_for client, peer - Signal.trap 'CHLD', 'DEFAULT' + def act_as_a_child_for peer + Signal.trap 'CHLD', 'SYSTEM_DEFAULT' $master = false remove_instance_variable :@workers $0 = sprintf '%s worker for %s', @arvg0, peer - Timeout.timeout(self.class.worker_timeout) { serve client } # FIXME shall move to master in the future + Timeout.timeout(self.class.worker_timeout) { serve peer } # FIXME shall move to master in the future or maybe drop altogether in favour of activity detection + rescue SignalException + $log.debug 'Trapped %p' % $!.signm + raise $! rescue Exception $!.log ensure + $log.debug 'Terminating' exit! end - def serve client + def serve peer require 'rbmk/worker' - Worker.hire client, @upstream + Worker.hire peer, @upstream + end + + def kill pid + $log.debug 'Killing worker %s' % pid + Process.kill 'TERM', pid + Process.wait pid + $log.debug 'Worker %s will not be a problem anymore' % pid + rescue Errno::ESRCH + $log.debug 'Somehow worker %s was not alive' % pid + rescue Errno::ECHILD + $log.debug 'Worker %s has suddenly disappeared' % pid + end + + def reap + pid, status = Process.wait2 -1, Process::WNOHANG + @workers.delete pid + $log.debug 'Reaped %s' % pid + rescue Errno::ECHILD + $log.debug 'Something went wrong, no dead workers to reap' end end end