Zbatery 0.2.0 - Unicorn/Rainbows! resync
[zbatery.git] / lib / zbatery.rb
blob0b4190644a18dea5a4ab0dde26626451509772f8
1 # -*- encoding: binary -*-
2 require 'rainbows'
4 module Zbatery
6   # current version of Zbatery
7   VERSION = "0.2.0"
9   class << self
11     # runs the Zbatery HttpServer with +app+ and +options+ and does
12     # not return until the server has exited.
13     def run(app, options = {})
14       HttpServer.new(app, options).start.join
15     end
16   end
18   Rainbows::Const::RACK_DEFAULTS["SERVER_SOFTWARE"] = "Zbatery #{VERSION}"
20   # true if our Ruby implementation supports unlinked files
21   UnlinkedIO = begin
22     tmp = Unicorn::Util.tmpio
23     tmp.chmod(0)
24     tmp.close
25     true
26   rescue
27     false
28   end
30   # we don't actually fork workers, but allow using the
31   # {before,after}_fork hooks found in Unicorn/Rainbows!
32   # config files...
33   FORK_HOOK = lambda { |_,_| }
35   class HttpServer < Rainbows::HttpServer
37     # this class is only used to avoid breaking Unicorn user switching
38     class DeadIO
39       def chown(*args); end
40     end
42     # only used if no concurrency model is specified
43     def worker_loop(worker)
44       init_worker_process(worker)
45       begin
46         ret = IO.select(LISTENERS, nil, nil, nil) and
47         ret.first.each do |sock|
48           begin
49             process_client(sock.accept_nonblock)
50           rescue Errno::EAGAIN, Errno::ECONNABORTED
51           end
52         end
53       rescue Errno::EINTR
54       rescue Errno::EBADF, TypeError
55         break
56       rescue => e
57         Rainbows::Error.listen_loop(e)
58       end while G.alive
59     end
61     # no-op
62     def maintain_worker_count; end
63     def init_self_pipe!; end
65     # can't just do a graceful exit if reopening logs fails, so we just
66     # continue on...
67     def reopen_logs
68       logger.info "reopening logs"
69       Unicorn::Util.reopen_logs
70       logger.info "done reopening logs"
71       rescue => e
72         logger.error "failed reopening logs #{e.message}"
73     end
75     def trap_deferred(sig)
76       # nothing
77     end
79     def join
80       begin
81         trap(:INT) { stop(false) } # Mongrel trapped INT for Win32...
83         # try these anyways regardless of platform...
84         trap(:TERM) { stop(false) }
85         trap(:QUIT) { stop }
86         trap(:USR1) { reopen_logs }
87         trap(:USR2) { reexec }
89         # no other way to reliably switch concurrency models...
90         trap(:HUP) { reexec; stop }
92         # technically feasible in some cases, just not sanely supportable:
93         %w(TTIN TTOU WINCH).each do |sig|
94           trap(sig) { logger.info "SIG#{sig} is not handled by Zbatery" }
95         end
96       rescue => e # hopefully ignores errors on Win32...
97         logger.error "failed to setup signal handler: #{e.message}"
98       end
100       if ready_pipe
101         ready_pipe.syswrite($$.to_s)
102         ready_pipe.close rescue nil
103         self.ready_pipe = nil
104       end
106       worker = Worker.new(0, DeadIO.new)
107       before_fork.call(self, worker)
108       worker_loop(worker) # runs forever
109     end
111     def stop(graceful = true)
112       Rainbows::G.quit!
113       exit!(0) unless graceful
114     end
116     def before_fork
117       hook = super
118       hook == FORK_HOOK or
119         logger.warn "calling before_fork without forking"
120       hook
121     end
123     def after_fork
124       hook = super
125       hook == FORK_HOOK or
126         logger.warn "calling after_fork without having forked"
127       hook
128     end
129   end
132 # :stopdoc:
133 # override stuff we don't need or can't use portably
134 module Rainbows
136   module Base
137     # master == worker in our case
138     def init_worker_process(worker)
139       after_fork.call(self, worker)
140       worker.user(*user) if user.kind_of?(Array) && ! worker.switched
141       build_app! unless preload_app
143       # avoid spurious wakeups and blocking-accept() with 1.8 green threads
144       if RUBY_VERSION.to_f < 1.9
145         require "io/nonblock"
146         HttpServer::LISTENERS.each { |l| l.nonblock = true }
147       end
149       logger.info "Zbatery #@use worker_connections=#@worker_connections"
150     end
151   end
153   # we can't/don't need to do the fchmod heartbeat Unicorn/Rainbows! does
154   def G.tick
155     alive
156   end
159 module Unicorn
161   class Configurator
162     DEFAULTS[:before_fork] = DEFAULTS[:after_fork] = Zbatery::FORK_HOOK
163   end
165   unless Zbatery::UnlinkedIO
166     require 'tempfile'
167     class Util
169       # Tempfiles should get automatically unlinked by GC
170       def self.tmpio
171         fp = Tempfile.new("zbatery")
172         fp.binmode
173         fp.sync = true
174         fp
175       end
176     end
177   end