initial
[zbatery.git] / lib / zbatery.rb
blob0b9b553e65179caef61ca7214567c1b7e75048f0
1 # -*- encoding: binary -*-
2 require 'rainbows'
4 module Zbatery
6   # check if our Ruby implementation supports unlinked files
7   UnlinkedIO = begin
8     tmp = Unicorn::Util.tmpio
9     tmp.chmod(0)
10     tmp.close
11     true
12   rescue
13     false
14   end
16   # we don't actually fork workers, but allow using the
17   # {before,after}_fork hooks found in Unicorn/Rainbows!
18   # config files...
19   FORK_HOOK = lambda { |_,_| }
21   class << self
23     # runs the Rainbows! HttpServer with +app+ and +options+ and does
24     # not return until the server has exited.
25     def run(app, options = {})
26       HttpServer.new(app, options).start.join
27     end
28   end
30   class HttpServer < Rainbows::HttpServer
32     # only used if no concurrency model is specified
33     def worker_loop(worker)
34       init_worker_process(worker)
35       begin
36         ret = IO.select(LISTENERS, nil, nil, nil) and
37         ret.first.each do |sock|
38           begin
39             process_client(sock.accept_nonblock)
40           rescue Errno::EAGAIN, Errno::ECONNABORTED
41           end
42         end
43       rescue Errno::EINTR
44       rescue Errno::EBADF, TypeError
45         break
46       rescue => e
47         Rainbows::Error.listen_loop(e)
48       end while G.alive
49     end
51     # no-op
52     def maintain_worker_count; end
54     # can't just do a graceful exit if reopening logs fails, so we just
55     # continue on...
56     def reopen_logs
57       logger.info "reopening logs"
58       Unicorn::Util.reopen_logs
59       logger.info "done reopening logs"
60       rescue => e
61         logger.error "failed reopening logs #{e.message}"
62     end
64     def join
65       begin
66         trap(:INT) { stop(false) } # Mongrel trapped INT for Win32...
68         # try these anyways regardless of platform...
69         trap(:TERM) { stop(false) }
70         trap(:QUIT) { stop }
71         trap(:USR1) { reopen_logs }
72         trap(:USR2) { reexec }
74         # no other way to reliably switch concurrency models...
75         trap(:HUP) { reexec; stop }
77         # technically feasible in some cases, just not sanely supportable:
78         trap(:TTIN) { logger.info "SIGTTIN is not handled by Zbatery" }
79         trap(:TTOU) { logger.info "SIGTTOU is not handled by Zbatery" }
80       rescue => e # hopefully ignores errors on Win32...
81         logger.error "failed to setup signal handler: #{e.message}"
82       end
83       worker = Worker.new(0, $stdout)
84       before_fork.call(self, worker)
85       worker_loop(worker) # runs forever
86     end
88     def stop(graceful = true)
89       Rainbows::G.quit!
90       exit!(0) unless graceful
91     end
93     def before_fork
94       hook = super
95       hook == FORK_HOOK or
96         logger.warn "calling before_fork without forking"
97       hook
98     end
100     def after_fork
101       hook = super
102       hook == FORK_HOOK or
103         logger.warn "calling after_fork without having forked"
104       hook
105     end
106   end
109 # :stopdoc:
110 # override stuff we don't need or can't use portably
111 module Rainbows
113   module Base
114     # master == worker in our case
115     def init_worker_process(worker)
116       after_fork.call(self, worker)
117       build_app! unless preload_app
118       logger.info "Zbatery #@use worker_connections=#@worker_connections"
119     end
120   end
122   # we can't/don't need to do the fchmod heartbeat Unicorn/Rainbows! does
123   def G.tick
124     alive
125   end
128 module Unicorn
130   class Configurator
131     DEFAULTS[:before_fork] = DEFAULTS[:after_fork] = Zbatery::FORK_HOOK
132   end
134   unless Zbatery::UnlinkedIO
135     require 'tempfile'
136     class Util
138       # Tempfiles should get automatically unlinked by GC
139       def self.tmpio
140         fp = Tempfile.new("zbatery")
141         fp.binmode
142         fp.sync = true
143         fp
144       end
145     end
146   end