diff options
author | Eric Wong <normalperson@yhbt.net> | 2009-11-27 15:08:19 -0800 |
---|---|---|
committer | Eric Wong <normalperson@yhbt.net> | 2009-11-27 17:03:58 -0800 |
commit | 0e278d23faa3a07bc2fc7e473b29af6429d6aeef (patch) | |
tree | 5d5964529c1c838091d53f58078207f176952fa4 /lib/zbatery.rb | |
download | zbatery-0e278d23faa3a07bc2fc7e473b29af6429d6aeef.tar.gz |
initial
Diffstat (limited to 'lib/zbatery.rb')
-rw-r--r-- | lib/zbatery.rb | 148 |
1 files changed, 148 insertions, 0 deletions
diff --git a/lib/zbatery.rb b/lib/zbatery.rb new file mode 100644 index 0000000..0b9b553 --- /dev/null +++ b/lib/zbatery.rb @@ -0,0 +1,148 @@ +# -*- encoding: binary -*- +require 'rainbows' + +module Zbatery + + # check if our Ruby implementation supports unlinked files + UnlinkedIO = begin + tmp = Unicorn::Util.tmpio + tmp.chmod(0) + tmp.close + true + rescue + false + end + + # we don't actually fork workers, but allow using the + # {before,after}_fork hooks found in Unicorn/Rainbows! + # config files... + FORK_HOOK = lambda { |_,_| } + + class << self + + # runs the Rainbows! HttpServer with +app+ and +options+ and does + # not return until the server has exited. + def run(app, options = {}) + HttpServer.new(app, options).start.join + end + end + + class HttpServer < Rainbows::HttpServer + + # only used if no concurrency model is specified + def worker_loop(worker) + init_worker_process(worker) + begin + ret = IO.select(LISTENERS, nil, nil, nil) and + ret.first.each do |sock| + begin + process_client(sock.accept_nonblock) + rescue Errno::EAGAIN, Errno::ECONNABORTED + end + end + rescue Errno::EINTR + rescue Errno::EBADF, TypeError + break + rescue => e + Rainbows::Error.listen_loop(e) + end while G.alive + end + + # no-op + def maintain_worker_count; end + + # can't just do a graceful exit if reopening logs fails, so we just + # continue on... + def reopen_logs + logger.info "reopening logs" + Unicorn::Util.reopen_logs + logger.info "done reopening logs" + rescue => e + logger.error "failed reopening logs #{e.message}" + end + + def join + begin + trap(:INT) { stop(false) } # Mongrel trapped INT for Win32... + + # try these anyways regardless of platform... + trap(:TERM) { stop(false) } + trap(:QUIT) { stop } + trap(:USR1) { reopen_logs } + trap(:USR2) { reexec } + + # no other way to reliably switch concurrency models... + trap(:HUP) { reexec; stop } + + # technically feasible in some cases, just not sanely supportable: + trap(:TTIN) { logger.info "SIGTTIN is not handled by Zbatery" } + trap(:TTOU) { logger.info "SIGTTOU is not handled by Zbatery" } + rescue => e # hopefully ignores errors on Win32... + logger.error "failed to setup signal handler: #{e.message}" + end + worker = Worker.new(0, $stdout) + before_fork.call(self, worker) + worker_loop(worker) # runs forever + end + + def stop(graceful = true) + Rainbows::G.quit! + exit!(0) unless graceful + end + + def before_fork + hook = super + hook == FORK_HOOK or + logger.warn "calling before_fork without forking" + hook + end + + def after_fork + hook = super + hook == FORK_HOOK or + logger.warn "calling after_fork without having forked" + hook + end + end +end + +# :stopdoc: +# override stuff we don't need or can't use portably +module Rainbows + + module Base + # master == worker in our case + def init_worker_process(worker) + after_fork.call(self, worker) + build_app! unless preload_app + logger.info "Zbatery #@use worker_connections=#@worker_connections" + end + end + + # we can't/don't need to do the fchmod heartbeat Unicorn/Rainbows! does + def G.tick + alive + end +end + +module Unicorn + + class Configurator + DEFAULTS[:before_fork] = DEFAULTS[:after_fork] = Zbatery::FORK_HOOK + end + + unless Zbatery::UnlinkedIO + require 'tempfile' + class Util + + # Tempfiles should get automatically unlinked by GC + def self.tmpio + fp = Tempfile.new("zbatery") + fp.binmode + fp.sync = true + fp + end + end + end + +end |