Parent

Class/Module Index [+]

Quicksearch

PhusionPassenger::RequestHandler

Constants

BACKLOG_SIZE
HARD_TERMINATION_SIGNAL

Signal which will cause the Rails application to exit immediately.

SOFT_TERMINATION_SIGNAL

Signal which will cause the Rails application to exit as soon as it's done processing a request.

Attributes

concurrency[R]
connect_password[RW]

A password with which clients must authenticate. Default is unauthenticated.

server_sockets[R]

A hash containing all server sockets that this request handler listens on. The hash is in the form of:

{
   name1 => [socket_address1, socket_type1, socket1],
   name2 => [socket_address2, socket_type2, socket2],
   ...
}

name is a Symbol. socket_addressx is the address of the socket, socket_typex is the socket's type (either 'unix' or 'tcp') and socketx is the actual socket IO objec. There's guaranteed to be at least one server socket, namely one with the name :main.

soft_termination_linger_time[RW]

If a soft termination signal was received, then the main loop will quit the given amount of seconds after the last time a connection was accepted. Defaults to 3 seconds.

Public Class Methods

new(owner_pipe, options = {}) click to toggle source

Create a new RequestHandler with the given owner pipe. owner_pipe must be the readable part of a pipe IO object.

Additionally, the following options may be given:

  • detach_key

  • connect_password

  • pool_account_username

  • pool_account_password_base64

# File lib/phusion_passenger/request_handler.rb, line 87
def initialize(owner_pipe, options = {})
        require_option(options, "app_group_name")
        install_options_as_ivars(self, options,
                "app",
                "app_group_name",
                "connect_password",
                "detach_key",
                "analytics_logger",
                "pool_account_username"
        )
        @thread_handler = options["thread_handler"] || ThreadHandler
        @concurrency = 1
        if options["pool_account_password_base64"]
                @pool_account_password = options["pool_account_password_base64"].unpack('m').first
        end

        #############
        #############

        @server_sockets = {}
        
        if should_use_unix_sockets?
                @main_socket_address, @main_socket = create_unix_socket_on_filesystem
        else
                @main_socket_address, @main_socket = create_tcp_socket
        end
        @server_sockets[:main] = {
                :address     => @main_socket_address,
                :socket      => @main_socket,
                :protocol    => :session,
                :concurrency => @concurrency
        }

        @http_socket_address, @http_socket = create_tcp_socket
        @server_sockets[:http] = {
                :address     => @http_socket_address,
                :socket      => @http_socket,
                :protocol    => :http,
                :concurrency => 1
        }
        
        @owner_pipe = owner_pipe
        @options = options
        @previous_signal_handlers = {}
        @main_loop_generation  = 0
        @main_loop_thread_lock = Mutex.new
        @main_loop_thread_cond = ConditionVariable.new
        @threads = []
        @threads_mutex = Mutex.new
        @soft_termination_linger_time = 3
        @main_loop_running  = false
        
        #############
end

Public Instance Methods

cleanup() click to toggle source

Clean up temporary stuff created by the request handler.

If the main loop was started by main_loop, then this method may only be called after the main loop has exited.

If the main loop was started by start_main_loop_thread, then this method may be called at any time, and it will stop the main loop thread.

# File lib/phusion_passenger/request_handler.rb, line 149
def cleanup
        if @main_loop_thread
                @main_loop_thread_lock.synchronize do
                        @graceful_termination_pipe[1].close rescue nil
                end
                @main_loop_thread.join
        end
        @server_sockets.each_value do |value|
                address, type, socket = value
                socket.close rescue nil
                if type == 'unix'
                        File.unlink(address) rescue nil
                end
        end
        @owner_pipe.close rescue nil
end
main_loop() click to toggle source

Enter the request handler's main loop.

# File lib/phusion_passenger/request_handler.rb, line 174
def main_loop
        debug("Entering request handler main loop")
        reset_signal_handlers
        begin
                @graceful_termination_pipe = IO.pipe
                @graceful_termination_pipe[0].close_on_exec!
                @graceful_termination_pipe[1].close_on_exec!
                
                @main_loop_thread_lock.synchronize do
                        @main_loop_generation += 1
                        @main_loop_running = true
                        @main_loop_thread_cond.broadcast
                        
                        @select_timeout = nil
                        
                        @selectable_sockets = []
                        @server_sockets.each_value do |value|
                                socket = value[2]
                                @selectable_sockets << socket if socket
                        end
                        @selectable_sockets << @owner_pipe
                        @selectable_sockets << @graceful_termination_pipe[0]
                end
                
                install_useful_signal_handlers
                start_threads
                wait_until_termination_requested
                wait_until_all_threads_are_idle
                terminate_threads
                debug("Request handler main loop exited normally")

        rescue EOFError
                # Exit main loop.
                trace(2, "Request handler main loop interrupted by EOFError exception")
        rescue Interrupt
                # Exit main loop.
                trace(2, "Request handler main loop interrupted by Interrupt exception")
        rescue SignalException => signal
                trace(2, "Request handler main loop interrupted by SignalException")
                if signal.message != HARD_TERMINATION_SIGNAL &&
                   signal.message != SOFT_TERMINATION_SIGNAL
                        raise
                end
        rescue Exception => e
                trace(2, "Request handler main loop interrupted by #{e.class} exception")
                raise
        ensure
                debug("Exiting request handler main loop")
                revert_signal_handlers
                @main_loop_thread_lock.synchronize do
                        @graceful_termination_pipe[1].close rescue nil
                        @graceful_termination_pipe[0].close rescue nil
                        @selectable_sockets = []
                        @main_loop_generation += 1
                        @main_loop_running = false
                        @main_loop_thread_cond.broadcast
                end
        end
end
main_loop_running?() click to toggle source

Check whether the main loop's currently running.

# File lib/phusion_passenger/request_handler.rb, line 167
def main_loop_running?
        @main_loop_thread_lock.synchronize do
                return @main_loop_running
        end
end
soft_shutdown() click to toggle source

Remove this request handler from the application pool so that no new connections will come in. Then make the main loop quit a few seconds after the last time a connection came in. This all is to ensure that no connections come in while we're shutting down.

May only be called while the main loop is running. May be called from any thread.

# File lib/phusion_passenger/request_handler.rb, line 258
def soft_shutdown
        @soft_termination_linger_thread ||= Thread.new do
                debug("Soft termination initiated")
                if @detach_key && @pool_account_username && @pool_account_password
                        client = MessageClient.new(@pool_account_username, @pool_account_password)
                        begin
                                client.pool_detach_process_by_key(@detach_key)
                        ensure
                                client.close
                        end
                end
                wait_until_all_threads_are_idle
                debug("Soft terminating in #{@soft_termination_linger_time} seconds")
                sleep @soft_termination_linger_time
                @graceful_termination_pipe[1].close rescue nil
        end
end
start_main_loop_thread() click to toggle source

Start the main loop in a new thread. This thread will be stopped by cleanup.

# File lib/phusion_passenger/request_handler.rb, line 235
def start_main_loop_thread
        current_generation = @main_loop_generation
        @main_loop_thread = Thread.new do
                begin
                        main_loop
                rescue Exception => e
                        print_exception(self.class, e)
                end
        end
        @main_loop_thread_lock.synchronize do
                while @main_loop_generation == current_generation
                        @main_loop_thread_cond.wait(@main_loop_thread_lock)
                end
        end
end

[Validate]

Generated with the Darkfish Rdoc Generator 2.