Class Merb::Rack::AbstractAdapter
In: merb-core/lib/merb-core/rack/adapter/abstract.rb
Parent: Object

Methods

Public Class methods

Exit the process with the specified status.

Parameters

status<Integer>:The exit code of the process.

:api: private

[Source]

     # File merb-core/lib/merb-core/rack/adapter/abstract.rb, line 307
307:       def self.exit_process(status = 0)
308:         exit(status)
309:       end

This method is designed to be overridden in a rack adapter. It will be called to create a new instance of the server for the adapter to start. The adapter should attempt to bind to a port at this point. This is called from the AbstractAdapter start method.

Parameters

port<Integer>:The port the server should listen on

:api: plugin @overridable

[Source]

    # File merb-core/lib/merb-core/rack/adapter/abstract.rb, line 68
68:       def self.new_server(port)
69:         raise NotImplemented
70:       end

Set the process title.

Parameters

whoami<Symbol>:Either :spawner for the master process or :worker for any of the worker
  processes.
port<Integer>:The base port that the app is running on.

:api: private

[Source]

     # File merb-core/lib/merb-core/rack/adapter/abstract.rb, line 319
319:       def self.process_title(whoami, port)
320:         name = Merb::Config[:name]
321:         app  = "merb#{" : #{name}" if (name && name != "merb")}"
322:         max_port  = Merb::Config[:cluster] ? (Merb::Config[:cluster] - 1) : 0
323:         numbers   = ((whoami != :worker) && (max_port > 0)) ? "#{port}..#{port + max_port}" : port
324:         file      = Merb::Config[:socket_file] % port if Merb::Config[:socket_file]
325:         
326:         listening_on = if Merb::Config[:socket]
327:           "socket#{'s' if max_port > 0 && whoami != :worker} #{numbers} "\
328:           "#{file ? file : "#{Merb.log_path}/#{name}.#{port}.sock"}"
329:         else
330:           "port#{'s' if max_port > 0 && whoami != :worker} #{port}"
331:         end
332:         "#{app} : #{whoami} (#{listening_on})"
333:       end

Spawn a new worker process at a port.

Parameters

port<Integer>:The port to start the worker process on.

:api: private

[Source]

     # File merb-core/lib/merb-core/rack/adapter/abstract.rb, line 93
 93:       def self.spawn_worker(port)
 94:         worker_pid = Kernel.fork
 95:         start_at_port(port, @opts) unless worker_pid
 96: 
 97:         # If we have a worker_pid, we're in the parent.
 98:         throw(:new_worker) unless worker_pid
 99: 
100:         @pids[port] = worker_pid
101:         $WORKERS = @pids.values
102:       end

The main start method for bootloaders that support forking. This method launches the adapters which inherit using the new_server and start_server methods. This method should not be overridden in adapters which want to fork.

Parameters

opts<Hash>:A hash of options
  socket: the socket to bind to
  port: the port to bind to
  cluster: the number

:api: private

[Source]

     # File merb-core/lib/merb-core/rack/adapter/abstract.rb, line 116
116:       def self.start(opts={})
117:         @opts = opts
118:         $WORKERS ||= []
119:         parent = nil
120: 
121:         @pids = {}
122:         port = (opts[:socket] || opts[:port]).to_i
123:         max_port = Merb::Config[:cluster] ? Merb::Config[:cluster] - 1 : 0
124: 
125:         # If we only have a single merb, just start it up and dispense with
126:         # the spawner/worker setup.
127:         if max_port == 0
128:           start_at_port(port)
129:           return
130:         end
131: 
132:         $0 = process_title(:spawner, port)
133: 
134:         # For each port, spawn a new worker. The parent will continue in
135:         # the loop, while the worker will throw :new_worker and be booted
136:         # out of the loop.
137:         catch(:new_worker) do
138:           0.upto(max_port) do |i|
139:             parent = spawn_worker(port + i)
140:           end
141:         end
142: 
143:         # If we're in a worker, we're done. Otherwise, we've completed
144:         # setting up workers and now need to watch them.
145:         return unless parent
146: 
147:         # For each worker, set up a thread in the spawner to watch it
148:         0.upto(max_port) do |i|
149:           Thread.new do
150:             catch(:new_worker) do
151:               loop do
152:                 pid, status = @pids[port + i], nil
153:                 poller = Merb::System::PortablePoller.new(pid)
154:                 begin
155:                   tick = 1
156:                   loop do                    
157:                     # Watch for the pid to exit.
158:                     _, status = Process.wait2(pid, Process::WNOHANG)
159:                     break if status
160:                     
161:                     if (tick % 120 == 0) && Merb::Config[:max_memory] && poller.memory > Merb::Config[:max_memory]
162:                       tick = 1
163:                       Process.kill("INT", pid)
164:                       if (Process.kill(0, pid) rescue false)
165:                         sleep Merb::Config[:hang_time] || 5
166:                         Process.kill(9, pid)
167:                         Process.wait2(pid) if (Process.kill(0, pid) rescue false)
168:                       end
169:                       
170:                       status = Struct.new(:exitstatus).new(nil)
171:                       break
172:                     end
173:                     tick += 1
174:                     sleep 0.25
175:                   end
176: 
177:                   # If the pid doesn't exist, we want to silently exit instead of
178:                   # raising here.
179:                 rescue SystemCallError => e
180:                 ensure
181:                   # If there was no worker with that PID, the status was non-0
182:                   # (we send back a status of 128 when ABRT is called on a 
183:                   # worker, and Merb.fatal! exits with a status of 1), or if
184:                   # Merb is in the process of exiting, *then* don't respawn.
185:                   # Note that processes killed with kill -9 will return no
186:                   # exitstatus, and we respawn them.
187:                   if !status || 
188:                     (status.exitstatus && status.exitstatus != 0) || 
189:                     Merb.exiting then
190:                     Thread.exit
191:                   end
192:                 end
193: 
194:                 # Otherwise, respawn the worker, and watch it again.
195:                 spawn_worker(port + i)
196:               end
197:             end
198:           end
199:         end
200: 
201:         # The spawner process will make it here, and when it does, it should just 
202:         # sleep so it can pick up ctrl-c if it's in console mode.
203:         sleep
204: 
205:       end

Fork a server on the specified port and start the app.

Parameters

port<Integer>:The port to start the server on
opts<Hash>:The hash of options, defaults to the @opts
  instance variable.

:api: private

[Source]

     # File merb-core/lib/merb-core/rack/adapter/abstract.rb, line 215
215:       def self.start_at_port(port, opts = @opts)
216:         at_exit do
217:           Merb::Server.remove_pid(port)
218:         end
219: 
220:         Merb::Worker.start unless Merb.testing?
221: 
222:         # If Merb is daemonized, trap INT. If it's not daemonized,
223:         # we let the master process' ctrl-c control the cluster
224:         # of workers.
225:         if Merb::Config[:daemonize]
226:           Merb.trap('INT') do
227:             Merb.exiting = true
228:             stop
229:             Merb.logger.warn! "Exiting port #{port}\n"
230:             exit_process
231:           end
232:           # If it was not fork_for_class_load, we already set up
233:           # ctrl-c handlers in the master thread.
234:         elsif Merb::Config[:fork_for_class_load]
235:           if Merb::Config[:console_trap]
236:             Merb::Server.add_irb_trap
237:           end
238:         end
239: 
240:         # In daemonized mode or not, support HUPing the process to
241:         # restart it.
242:         Merb.trap('HUP') do
243:           Merb.exiting = true
244:           stop
245:           Merb.logger.warn! "Exiting port #{port} on #{Process.pid}\n"
246:           exit_process
247:         end
248: 
249:         # ABRTing the process will kill it, and it will not be respawned.
250:         Merb.trap('ABRT') do
251:           Merb.exiting = true
252:           stopped = stop(128)
253:           Merb.logger.warn! "Exiting port #{port}\n" if stopped
254:           exit_process(128)
255:         end
256: 
257:         # Each worker gets its own `ps' name.
258:         $0 = process_title(:worker, port)
259: 
260:         # Store the PID for this worker
261:         Merb::Server.store_pid(port)
262: 
263:         Merb::Config[:log_delimiter] = "#{process_title(:worker, port)} ~ "
264: 
265:         Merb.reset_logger!
266:         Merb.logger.warn!("Starting #{self.name.split("::").last} at port #{port}")
267: 
268:         # If we can't connect to the port, keep trying until we can. Print
269:         # a warning about this once. Try every 0.25s.
270:         printed_warning = false
271:         loop do
272:           begin
273:             # Call the adapter's new_server method, which should attempt
274:             # to bind to a port.
275:             new_server(port)
276:           rescue Errno::EADDRINUSE => e
277:             if Merb::Config[:bind_fail_fatal]
278:               Merb.fatal! "Could not bind to #{port}. It was already in use", e
279:             end
280:             
281:             unless printed_warning
282:               Merb.logger.warn! "Port #{port} is in use, " \
283:                 "Waiting for it to become available."
284:               printed_warning = true
285:             end
286: 
287:             sleep 0.25
288:             next
289:           end
290:           break
291:         end
292: 
293:         Merb.logger.warn! "Successfully bound to port #{port}"
294: 
295:         Merb::Server.change_privilege
296: 
297:         # Call the adapter's start_server method.
298:         start_server
299:       end

This method is designed to be overridden in a rack adapter. It will be called to start a server created with the new_server method. This is called from the AbstractAdapter start method.

:api: plugin @overridable

[Source]

    # File merb-core/lib/merb-core/rack/adapter/abstract.rb, line 54
54:       def self.start_server
55:         raise NotImplemented
56:       end

This method is designed to be overridden in a rack adapter. It will be called to stop the adapter server.

Parameters

status<Integer>:The exit status the adapter should exit with.

Returns

Boolean:True if the server was properly stopped.

:api: plugin @overridable

[Source]

    # File merb-core/lib/merb-core/rack/adapter/abstract.rb, line 83
83:       def self.stop(status)
84:         raise NotImplemented
85:       end

[Validate]