README: reference yahns
[zbatery.git] / lib / zbatery.rb
blob53cf5edcfd5c67249d6c0cce0c2a85ea06a36c95
1 # -*- encoding: binary -*-
2 # :enddoc:
3 require 'rainbows'
4 Rainbows.forked = true
5 module Zbatery
7   VERSION = "4.2.0"
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
20   @readers = [] # rainbows 4.6.x compatibility
22   module Base
23     # master == worker in our case
24     def init_worker_process(worker)
25       after_fork.call(self, worker)
26       worker.user(*user) if user.kind_of?(Array) && ! worker.switched
27       build_app! unless preload_app
28       Rainbows::Response.setup
29       Rainbows::MaxBody.setup
30       Rainbows::ProcessClient.const_set(:APP, @app)
32       logger.info "Zbatery #@use worker_connections=#@worker_connections"
33     end
34   end
36   # we can't/don't need to do the fchmod heartbeat Unicorn/Rainbows! does
37   def self.tick
38     alive
39   end
41   class HttpServer
43     # only used if no concurrency model is specified
44     def worker_loop(worker)
45       init_worker_process(worker)
46       begin
47         ret = IO.select(LISTENERS, nil, nil, nil) and
48         ret[0].each do |sock|
49           io = sock.kgio_tryaccept and process_client(io)
50         end
51       rescue Errno::EINTR
52       rescue Errno::EBADF, TypeError
53         break
54       rescue => e
55         Rainbows::Error.listen_loop(e)
56       end while Rainbows.alive
57     end
59     # no-op
60     def maintain_worker_count; end
61     def spawn_missing_workers; end
62     def init_self_pipe!; end
64     # can't just do a graceful exit if reopening logs fails, so we just
65     # continue on...
66     def reopen_logs
67       logger.info "reopening logs"
68       Unicorn::Util.reopen_logs
69       logger.info "done reopening logs"
70       rescue => e
71         logger.error "failed reopening logs #{e.message}"
72     end
74     def trap_deferred(sig)
75       # nothing
76     end
78     def join
79       at_exit { unlink_pid_safe(pid) if pid }
80       trap(:INT) { exit!(0) }
81       trap(:TERM) { exit!(0) }
82       trap(:QUIT) { Thread.new { stop } }
83       trap(:USR1) { Thread.new { reopen_logs } }
84       trap(:USR2) { Thread.new { reexec } }
85       trap(:HUP) { Thread.new { reexec; stop } }
86       trap(:CHLD, "DEFAULT")
88       # technically feasible in some cases, just not sanely supportable:
89       %w(TTIN TTOU WINCH).each do |sig|
90         trap(sig) do
91           Thread.new { logger.info("SIG#{sig} is not handled by Zbatery") }
92         end
93       end
95       if ready_pipe
96         begin
97           ready_pipe.syswrite($$.to_s)
98         rescue => e
99           logger.warn("grandparent died too soon?: #{e.message} (#{e.class})")
100         end
101         ready_pipe.close
102         self.ready_pipe = nil
103       end
104       extend(Rainbows.const_get(@use))
105       worker = Worker.new(0)
106       before_fork.call(self, worker)
107       worker_loop(worker) # runs forever
108     end
110     def stop(graceful = true)
111       Rainbows.quit!
112       graceful ? exit : exit!(0)
113     end
115     def before_fork
116       hook = super
117       hook == Zbatery::FORK_HOOK or
118         logger.warn "calling before_fork without forking"
119       hook
120     end
122     def after_fork
123       hook = super
124       hook == Zbatery::FORK_HOOK or
125         logger.warn "calling after_fork without having forked"
126       hook
127     end
128   end
131 Unicorn::Configurator::DEFAULTS[:before_fork] =
132   Unicorn::Configurator::DEFAULTS[:after_fork] = Zbatery::FORK_HOOK