0
@@ -2,17 +2,19 @@ module Merb
0
+ # Spawn a new worker process at a port.
0
def self.spawn_worker(port)
0
- start_at_port(port, @opts) unless pid
0
+ child_pid = Kernel.fork
0
+ start_at_port(port, @opts) unless child_pid
0
- # pid means we're in the parent, which means continue the loop
0
- throw(:new_worker) unless pid
0
+ # If we have a child_pid, we're in the parent. If we're
0
+ throw(:new_worker) unless child_pid
0
+ @pids[port] =
child_pid
0
$CHILDREN = @pids.values
0
+ # The main start method for bootloaders that support forking.
0
def self.start(opts={})
0
@@ -24,6 +26,8 @@ module Merb
0
Merb.logger.warn! "Cluster: #{max_port}"
0
+ # If we only have a single merb, just start it up and dispense with
0
+ # the spawner/worker setup.
0
@@ -31,34 +35,49 @@ module Merb
0
+ # For each port, spawn a new worker. The parent will continue in
0
+ # the loop, while the child will throw :new_worker and be booted
0
0.upto(max_port) do |i|
0
parent = spawn_worker(port + i)
0
- # pid means we're in the parent, so start watching the children
0
- # no pid means we're in a child, so just move on
0
+ # If we're in a worker, we're done. Otherwise, we've completed
0
+ # setting up workers and now need to watch them.
0
+ # For each worker, set up a thread in the spawner to watch it
0
0.upto(max_port) do |i|
0
+ # Watch for the pid to exit.
0
_, status = Process.wait2(pid)
0
+ # If the pid doesn't exist, we want to silently exit instead of
0
rescue SystemCallError => e
0
+ # If there was no worker with that PID, the status was non-0
0
+ # (we send back a status of 128 when ABRT is called on a
0
+ # child, and Merb.fatal! exits with a status of 1), or if
0
+ # Merb is in the process of exiting, *then* don't respawn.
0
Thread.exit if !status || status.exitstatus != 0 || Merb.exiting
0
+ # Otherwise, respawn the worker, and watch it again.
0
+ # The spawner process will make it here, and when it does, it should just
0
+ # sleep so it can pick up ctrl-c if it's in console mode.
0
@@ -68,37 +87,56 @@ module Merb
0
Merb::Server.remove_pid(port)
0
+ # If Merb is daemonized, trap INT. If it's not daemonized,
0
+ # we let the master process' ctrl-c control the cluster
0
if Merb::Config[:daemonize]
0
Merb.logger.warn! "Exiting port #{port}\n"
0
+ # If it was not fork_for_class_load, we already set up
0
+ # ctrl-c handlers in the master thread.
0
elsif Merb::Config[:fork_for_class_load]
0
+ # In daemonized mode or not, support HUPing the process to
0
Merb.logger.warn! "Exiting port #{port} on #{Process.pid}\n"
0
+ # ABRTing the process will kill it, and it will not be respawned.
0
Merb.logger.warn! "Exiting port #{port}\n" if stopped
0
+ # Each worker gets its own `ps' name.
0
$0 = "merb: worker (port #{port})"
0
+ # Store the PID for this worker
0
Merb::Server.store_pid(port)
0
- Merb.logger = Merb::Logger.new(Merb.log_file(port), Merb::Config[:log_level], Merb::Config[:log_delimiter], Merb::Config[:log_auto_flush])
0
+ # Set up the logger for this worker to point to its process.
0
+ Merb.logger = Merb::Logger.new(Merb.log_file(port),
0
+ Merb::Config[:log_level], Merb::Config[:log_delimiter],
0
+ Merb::Config[:log_auto_flush])
0
Merb.logger.warn!("Starting #{self.name.split("::").last} at port #{port}")
0
+ # If we can't connect to the port, keep trying until we can. Print
0
+ # a warning about this once. Try every 0.25s.
0
printed_warning = false
0
+ # Call the adapter's new_server method, which should attempt
0
rescue Errno::EADDRINUSE
0
@@ -117,9 +155,11 @@ module Merb
0
Merb::Server.change_privilege
0
+ # Call the adapter's start_server method.
0
+ # This can be overridden in adapters, but shouldn't need to be.
0
def self.exit_process(status = 0)