Zbatery 4.1.2 - we don't fork, but our apps may!
[zbatery.git] / lib / zbatery.rb
blob65c9d0c3a6ae520360e944dcfe359fdaece2ca05
1 # -*- encoding: binary -*-
2 # :enddoc:
3 require 'rainbows'
4 Rainbows.forked = true
5 module Zbatery
7   VERSION = "4.1.2"
9   Rainbows::Const::RACK_DEFAULTS["SERVER_SOFTWARE"] = "Zbatery #{VERSION}"
11   # we don't actually fork workers, but allow using the
12   # {before,after}_fork hooks found in Unicorn/Rainbows!
13   # config files...
14   FORK_HOOK = lambda { |_,_| }
15 end
17 # :stopdoc:
18 # override stuff we don't need or can't use portably
19 module Rainbows
21   module Base
22     # master == worker in our case
23     def init_worker_process(worker)
24       after_fork.call(self, worker)
25       worker.user(*user) if user.kind_of?(Array) && ! worker.switched
26       build_app! unless preload_app
27       Rainbows::Response.setup
28       Rainbows::MaxBody.setup
29       Rainbows::ProcessClient.const_set(:APP, @app)
31       logger.info "Zbatery #@use worker_connections=#@worker_connections"
32     end
33   end
35   # we can't/don't need to do the fchmod heartbeat Unicorn/Rainbows! does
36   def self.tick
37     alive
38   end
40   class HttpServer
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[0].each do |sock|
48           io = sock.kgio_tryaccept and process_client(io)
49         end
50       rescue Errno::EINTR
51       rescue Errno::EBADF, TypeError
52         break
53       rescue => e
54         Rainbows::Error.listen_loop(e)
55       end while Rainbows.alive
56     end
58     # no-op
59     def maintain_worker_count; end
60     def spawn_missing_workers; end
61     def init_self_pipe!; end
63     # can't just do a graceful exit if reopening logs fails, so we just
64     # continue on...
65     def reopen_logs
66       logger.info "reopening logs"
67       Unicorn::Util.reopen_logs
68       logger.info "done reopening logs"
69       rescue => e
70         logger.error "failed reopening logs #{e.message}"
71     end
73     def trap_deferred(sig)
74       # nothing
75     end
77     def join
78       at_exit { unlink_pid_safe(pid) if pid }
79       trap(:INT) { exit!(0) }
80       trap(:TERM) { exit!(0) }
81       trap(:QUIT) { Thread.new { stop } }
82       trap(:USR1) { Thread.new { reopen_logs } }
83       trap(:USR2) { Thread.new { reexec } }
84       trap(:HUP) { Thread.new { reexec; stop } }
85       trap(:CHLD, "DEFAULT")
87       # technically feasible in some cases, just not sanely supportable:
88       %w(TTIN TTOU WINCH).each do |sig|
89         trap(sig) do
90           Thread.new { logger.info("SIG#{sig} is not handled by Zbatery") }
91         end
92       end
94       if ready_pipe
95         ready_pipe.syswrite($$.to_s)
96         ready_pipe.close
97         self.ready_pipe = nil
98       end
99       extend(Rainbows.const_get(@use))
100       worker = Worker.new(0)
101       before_fork.call(self, worker)
102       worker_loop(worker) # runs forever
103     end
105     def stop(graceful = true)
106       Rainbows.quit!
107       graceful ? exit : exit!(0)
108     end
110     def before_fork
111       hook = super
112       hook == Zbatery::FORK_HOOK or
113         logger.warn "calling before_fork without forking"
114       hook
115     end
117     def after_fork
118       hook = super
119       hook == Zbatery::FORK_HOOK or
120         logger.warn "calling after_fork without having forked"
121       hook
122     end
123   end
126 Unicorn::Configurator::DEFAULTS[:before_fork] =
127   Unicorn::Configurator::DEFAULTS[:after_fork] = Zbatery::FORK_HOOK