puma 2.2.2-java → 2.3.0-java
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of puma might be problematic. Click here for more details.
- data/History.txt +19 -0
- data/README.md +28 -8
- data/lib/puma/app/status.rb +21 -18
- data/lib/puma/binder.rb +12 -0
- data/lib/puma/cli.rb +185 -564
- data/lib/puma/configuration.rb +2 -2
- data/lib/puma/const.rb +2 -1
- data/lib/puma/control_cli.rb +16 -4
- data/lib/puma/delegation.rb +2 -2
- data/lib/puma/events.rb +14 -0
- data/lib/puma/server.rb +30 -19
- data/lib/puma/thread_pool.rb +2 -2
- data/puma.gemspec +2 -2
- data/test/test_app_status.rb +6 -2
- data/test/test_cli.rb +19 -17
- data/test/test_integration.rb +16 -5
- data/test/test_puma_server.rb +48 -3
- data/test/test_thread_pool.rb +3 -3
- metadata +2 -2
    
        data/History.txt
    CHANGED
    
    | @@ -1,3 +1,22 @@ | |
| 1 | 
            +
            === 2.3.0 / 2013-07-05
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            * 1 major bug fix:
         | 
| 4 | 
            +
             | 
| 5 | 
            +
              * Stabilize control server, add support in cluster mode
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            * 5 minor bug fixes:
         | 
| 8 | 
            +
             | 
| 9 | 
            +
              * Add ability to cleanup stale unix sockets
         | 
| 10 | 
            +
              * Check status data better. Fixes #292
         | 
| 11 | 
            +
              * Convert raw IO errors to ConnectionError. Fixes #274
         | 
| 12 | 
            +
              * Fix sending Content-Type and Content-Length for no body status. Fixes #304
         | 
| 13 | 
            +
              * Pass state path through to `pumactl start`. Fixes #287
         | 
| 14 | 
            +
             | 
| 15 | 
            +
            * 2 internal changes:
         | 
| 16 | 
            +
             | 
| 17 | 
            +
              * Refactored modes into seperate classes that CLI uses
         | 
| 18 | 
            +
              * Changed CLI to take an Events object instead of stdout/stderr (API change)
         | 
| 19 | 
            +
             | 
| 1 20 | 
             
            === 2.2.2 / 2013-07-02
         | 
| 2 21 |  | 
| 3 22 | 
             
            * 1 bug fix:
         | 
    
        data/README.md
    CHANGED
    
    | @@ -82,21 +82,41 @@ Puma 2 offers clustered mode, allowing you to use forked processes to handle mul | |
| 82 82 | 
             
            On a ruby implementation that offers native threads, you should tune this number to match the number of cores available. 
         | 
| 83 83 | 
             
            Note that threads are still used in clustered mode, and the `-t` thread flag setting is per worker, so `-w 2 -t 16:16` will be 32 threads.
         | 
| 84 84 |  | 
| 85 | 
            +
            This code can be used to setup the process before booting the application, allowing 
         | 
| 86 | 
            +
            you to do some puma-specific things that you don't want to embed in your application. 
         | 
| 87 | 
            +
            For instance, you could fire a log notification that a worker booted or send something to statsd. 
         | 
| 88 | 
            +
            This can be called multiple times to add hooks.
         | 
| 89 | 
            +
             | 
| 90 | 
            +
            If you're running in Clustered Mode you can optionally choose to preload your application before starting up the workers. To do this simply specify the `--preload` flag in invocation:
         | 
| 91 | 
            +
             | 
| 92 | 
            +
                # CLI invocation
         | 
| 93 | 
            +
                $ puma -t 8:32 -w 3 --preload
         | 
| 94 | 
            +
                
         | 
| 95 | 
            +
            If you're using a configuration file, use the `preload_app!` method, and be sure to specify your config file's location with the `-C` flag:
         | 
| 96 | 
            +
             | 
| 97 | 
            +
                $ puma -C config/puma.rb
         | 
| 98 | 
            +
                
         | 
| 99 | 
            +
                # config/puma.rb
         | 
| 100 | 
            +
                threads 8,32
         | 
| 101 | 
            +
                workers 3
         | 
| 102 | 
            +
                preload_app! 
         | 
| 103 | 
            +
             | 
| 104 | 
            +
             | 
| 85 105 | 
             
            Additionally, you can specify a block in your configuration that will be run on boot of each worker:
         | 
| 86 106 |  | 
| 87 107 | 
             
                # config/puma.rb
         | 
| 88 108 | 
             
                on_worker_boot do
         | 
| 89 109 | 
             
                  # configuration here
         | 
| 90 110 | 
             
                end
         | 
| 111 | 
            +
                    
         | 
| 112 | 
            +
            If you're preloading your application and using ActiveRecord, it's recommend you setup your connection pool here: 
         | 
| 91 113 |  | 
| 92 | 
            -
             | 
| 93 | 
            -
             | 
| 94 | 
            -
             | 
| 95 | 
            -
             | 
| 96 | 
            -
             | 
| 97 | 
            -
             | 
| 98 | 
            -
             | 
| 99 | 
            -
                $ puma -t 8:32 -w 3 -C config/puma.rb
         | 
| 114 | 
            +
                # config/puma.rb
         | 
| 115 | 
            +
                on_worker_boot do
         | 
| 116 | 
            +
                  ActiveSupport.on_load(:active_record) do
         | 
| 117 | 
            +
                    ActiveRecord::Base.establish_connection
         | 
| 118 | 
            +
                  end
         | 
| 119 | 
            +
                end
         | 
| 100 120 |  | 
| 101 121 | 
             
            ### Binding TCP / Sockets
         | 
| 102 122 |  | 
    
        data/lib/puma/app/status.rb
    CHANGED
    
    | @@ -1,8 +1,7 @@ | |
| 1 1 | 
             
            module Puma
         | 
| 2 2 | 
             
              module App
         | 
| 3 3 | 
             
                class Status
         | 
| 4 | 
            -
                  def initialize( | 
| 5 | 
            -
                    @server = server
         | 
| 4 | 
            +
                  def initialize(cli)
         | 
| 6 5 | 
             
                    @cli = cli
         | 
| 7 6 | 
             
                    @auth_token = nil
         | 
| 8 7 | 
             
                  end
         | 
| @@ -15,6 +14,15 @@ module Puma | |
| 15 14 | 
             
                    env['QUERY_STRING'].to_s.split(/&;/).include?("token=#{@auth_token}")
         | 
| 16 15 | 
             
                  end
         | 
| 17 16 |  | 
| 17 | 
            +
                  def rack_response(status, body, content_type='application/json')
         | 
| 18 | 
            +
                    headers = {
         | 
| 19 | 
            +
                      'Content-Type' => content_type,
         | 
| 20 | 
            +
                      'Content-Length' => body.bytesize.to_s
         | 
| 21 | 
            +
                    }
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                    [status, headers, [body]]
         | 
| 24 | 
            +
                  end
         | 
| 25 | 
            +
             | 
| 18 26 | 
             
                  def call(env)
         | 
| 19 27 | 
             
                    unless authenticate(env)
         | 
| 20 28 | 
             
                      return rack_response(403, 'Invalid auth token', 'text/plain')
         | 
| @@ -22,34 +30,29 @@ module Puma | |
| 22 30 |  | 
| 23 31 | 
             
                    case env['PATH_INFO']
         | 
| 24 32 | 
             
                    when /\/stop$/
         | 
| 25 | 
            -
                      @ | 
| 33 | 
            +
                      @cli.stop
         | 
| 26 34 | 
             
                      return rack_response(200, OK_STATUS)
         | 
| 27 35 |  | 
| 28 36 | 
             
                    when /\/halt$/
         | 
| 29 | 
            -
                      @ | 
| 37 | 
            +
                      @cli.halt
         | 
| 30 38 | 
             
                      return rack_response(200, OK_STATUS)
         | 
| 31 39 |  | 
| 32 40 | 
             
                    when /\/restart$/
         | 
| 33 | 
            -
                       | 
| 34 | 
            -
             | 
| 41 | 
            +
                      @cli.restart
         | 
| 42 | 
            +
                      return rack_response(200, OK_STATUS)
         | 
| 35 43 |  | 
| 36 | 
            -
             | 
| 44 | 
            +
                    when /\/phased-restart$/
         | 
| 45 | 
            +
                      if !@cli.phased_restart
         | 
| 46 | 
            +
                        return rack_response(404, '{ "error": "phased resart not available" }')
         | 
| 37 47 | 
             
                      else
         | 
| 38 | 
            -
                        return rack_response(200,  | 
| 48 | 
            +
                        return rack_response(200, OK_STATUS)
         | 
| 39 49 | 
             
                      end
         | 
| 40 50 |  | 
| 41 51 | 
             
                    when /\/stats$/
         | 
| 42 | 
            -
                       | 
| 43 | 
            -
             | 
| 44 | 
            -
                       | 
| 52 | 
            +
                      return rack_response(200, @cli.stats)
         | 
| 53 | 
            +
                    else
         | 
| 54 | 
            +
                      rack_response 404, "Unsupported action", 'text/plain'
         | 
| 45 55 | 
             
                    end
         | 
| 46 | 
            -
             | 
| 47 | 
            -
                    rack_response 404, "Unsupported action", 'text/plain'
         | 
| 48 | 
            -
                  end
         | 
| 49 | 
            -
             | 
| 50 | 
            -
                  private
         | 
| 51 | 
            -
                  def rack_response(status, body, content_type='application/json')
         | 
| 52 | 
            -
                    [status, { 'Content-Type' => content_type, 'Content-Length' => body.bytesize.to_s }, [body]]
         | 
| 53 56 | 
             
                  end
         | 
| 54 57 | 
             
                end
         | 
| 55 58 | 
             
              end
         | 
    
        data/lib/puma/binder.rb
    CHANGED
    
    | @@ -255,6 +255,18 @@ module Puma | |
| 255 255 |  | 
| 256 256 | 
             
                  begin
         | 
| 257 257 | 
             
                    old_mask = File.umask(umask)
         | 
| 258 | 
            +
             | 
| 259 | 
            +
                    if File.exists? path
         | 
| 260 | 
            +
                      begin
         | 
| 261 | 
            +
                        old = UNIXSocket.new path
         | 
| 262 | 
            +
                      rescue SystemCallError
         | 
| 263 | 
            +
                        File.unlink path
         | 
| 264 | 
            +
                      else
         | 
| 265 | 
            +
                        old.close
         | 
| 266 | 
            +
                        raise "There is already a server bound to: #{path}"
         | 
| 267 | 
            +
                      end
         | 
| 268 | 
            +
                    end
         | 
| 269 | 
            +
             | 
| 258 270 | 
             
                    s = UNIXServer.new(path)
         | 
| 259 271 | 
             
                    @ios << s
         | 
| 260 272 | 
             
                  ensure
         | 
    
        data/lib/puma/cli.rb
    CHANGED
    
    | @@ -8,6 +8,8 @@ require 'puma/binder' | |
| 8 8 | 
             
            require 'puma/detect'
         | 
| 9 9 | 
             
            require 'puma/daemon_ext'
         | 
| 10 10 | 
             
            require 'puma/util'
         | 
| 11 | 
            +
            require 'puma/single'
         | 
| 12 | 
            +
            require 'puma/cluster'
         | 
| 11 13 |  | 
| 12 14 | 
             
            require 'rack/commonlogger'
         | 
| 13 15 | 
             
            require 'rack/utils'
         | 
| @@ -22,122 +24,37 @@ module Puma | |
| 22 24 | 
             
                # +stdout+ and +stderr+ can be set to IO-like objects which
         | 
| 23 25 | 
             
                # this object will report status on.
         | 
| 24 26 | 
             
                #
         | 
| 25 | 
            -
                def initialize(argv,  | 
| 27 | 
            +
                def initialize(argv, events=Events.stdio)
         | 
| 26 28 | 
             
                  @debug = false
         | 
| 27 29 | 
             
                  @argv = argv
         | 
| 28 | 
            -
                  @stdout = stdout
         | 
| 29 | 
            -
                  @stderr = stderr
         | 
| 30 30 |  | 
| 31 | 
            -
                  @ | 
| 32 | 
            -
                  @workers = []
         | 
| 31 | 
            +
                  @events = events
         | 
| 33 32 |  | 
| 34 | 
            -
                  @events = Events.new @stdout, @stderr
         | 
| 35 | 
            -
             | 
| 36 | 
            -
                  @server = nil
         | 
| 37 33 | 
             
                  @status = nil
         | 
| 34 | 
            +
                  @runner = nil
         | 
| 38 35 |  | 
| 39 | 
            -
                  @ | 
| 40 | 
            -
                  @phased_state = :idle
         | 
| 41 | 
            -
             | 
| 42 | 
            -
                  @app = nil
         | 
| 36 | 
            +
                  @config = nil
         | 
| 43 37 |  | 
| 44 38 | 
             
                  ENV['NEWRELIC_DISPATCHER'] ||= "puma"
         | 
| 45 39 |  | 
| 46 40 | 
             
                  setup_options
         | 
| 47 | 
            -
             | 
| 48 41 | 
             
                  generate_restart_data
         | 
| 49 42 |  | 
| 50 43 | 
             
                  @binder = Binder.new(@events)
         | 
| 51 44 | 
             
                  @binder.import_from_env
         | 
| 52 45 | 
             
                end
         | 
| 53 46 |  | 
| 54 | 
            -
                 | 
| 55 | 
            -
             | 
| 56 | 
            -
                end
         | 
| 57 | 
            -
             | 
| 58 | 
            -
                def generate_restart_data
         | 
| 59 | 
            -
                  # Use the same trick as unicorn, namely favor PWD because
         | 
| 60 | 
            -
                  # it will contain an unresolved symlink, useful for when
         | 
| 61 | 
            -
                  # the pwd is /data/releases/current.
         | 
| 62 | 
            -
                  if dir = ENV['PWD']
         | 
| 63 | 
            -
                    s_env = File.stat(dir)
         | 
| 64 | 
            -
                    s_pwd = File.stat(Dir.pwd)
         | 
| 65 | 
            -
             | 
| 66 | 
            -
                    if s_env.ino == s_pwd.ino and s_env.dev == s_pwd.dev
         | 
| 67 | 
            -
                      @restart_dir = dir
         | 
| 68 | 
            -
                      @options[:worker_directory] = dir
         | 
| 69 | 
            -
                    end
         | 
| 70 | 
            -
                  end
         | 
| 71 | 
            -
             | 
| 72 | 
            -
                  @restart_dir ||= Dir.pwd
         | 
| 73 | 
            -
             | 
| 74 | 
            -
                  @original_argv = ARGV.dup
         | 
| 75 | 
            -
             | 
| 76 | 
            -
                  if defined? Rubinius::OS_ARGV
         | 
| 77 | 
            -
                    @restart_argv = Rubinius::OS_ARGV
         | 
| 78 | 
            -
                  else
         | 
| 79 | 
            -
                    require 'rubygems'
         | 
| 80 | 
            -
             | 
| 81 | 
            -
                    # if $0 is a file in the current directory, then restart
         | 
| 82 | 
            -
                    # it the same, otherwise add -S on there because it was
         | 
| 83 | 
            -
                    # picked up in PATH.
         | 
| 84 | 
            -
                    #
         | 
| 85 | 
            -
                    if File.exists?($0)
         | 
| 86 | 
            -
                      arg0 = [Gem.ruby, $0]
         | 
| 87 | 
            -
                    else
         | 
| 88 | 
            -
                      arg0 = [Gem.ruby, "-S", $0]
         | 
| 89 | 
            -
                    end
         | 
| 90 | 
            -
             | 
| 91 | 
            -
                    # Detect and reinject -Ilib from the command line
         | 
| 92 | 
            -
                    lib = File.expand_path "lib"
         | 
| 93 | 
            -
                    arg0[1,0] = ["-I", lib] if $:[0] == lib
         | 
| 94 | 
            -
             | 
| 95 | 
            -
                    @restart_argv = arg0 + ARGV
         | 
| 96 | 
            -
                  end
         | 
| 97 | 
            -
                end
         | 
| 98 | 
            -
             | 
| 99 | 
            -
                def restart_args
         | 
| 100 | 
            -
                  if cmd = @options[:restart_cmd]
         | 
| 101 | 
            -
                    cmd.split(' ') + @original_argv
         | 
| 102 | 
            -
                  else
         | 
| 103 | 
            -
                    @restart_argv
         | 
| 104 | 
            -
                  end
         | 
| 105 | 
            -
                end
         | 
| 106 | 
            -
             | 
| 107 | 
            -
                def restart!
         | 
| 108 | 
            -
                  @options[:on_restart].each do |blk|
         | 
| 109 | 
            -
                    blk.call self
         | 
| 110 | 
            -
                  end
         | 
| 47 | 
            +
                # The Binder object containing the sockets bound to.
         | 
| 48 | 
            +
                attr_reader :binder
         | 
| 111 49 |  | 
| 112 | 
            -
             | 
| 113 | 
            -
             | 
| 114 | 
            -
                      io.close
         | 
| 50 | 
            +
                # The Configuration object used.
         | 
| 51 | 
            +
                attr_reader :config
         | 
| 115 52 |  | 
| 116 | 
            -
             | 
| 117 | 
            -
             | 
| 118 | 
            -
                      if uri.scheme == "unix"
         | 
| 119 | 
            -
                        path = "#{uri.host}#{uri.path}"
         | 
| 120 | 
            -
                        File.unlink path
         | 
| 121 | 
            -
                      end
         | 
| 122 | 
            -
                    end
         | 
| 53 | 
            +
                # The Hash of options used to configure puma.
         | 
| 54 | 
            +
                attr_reader :options
         | 
| 123 55 |  | 
| 124 | 
            -
             | 
| 125 | 
            -
             | 
| 126 | 
            -
                  else
         | 
| 127 | 
            -
                    redirects = {:close_others => true}
         | 
| 128 | 
            -
                    @binder.listeners.each_with_index do |(l,io),i|
         | 
| 129 | 
            -
                      ENV["PUMA_INHERIT_#{i}"] = "#{io.to_i}:#{l}"
         | 
| 130 | 
            -
                      redirects[io.to_i] = io.to_i
         | 
| 131 | 
            -
                    end
         | 
| 132 | 
            -
             | 
| 133 | 
            -
                    argv = restart_args
         | 
| 134 | 
            -
             | 
| 135 | 
            -
                    Dir.chdir @restart_dir
         | 
| 136 | 
            -
             | 
| 137 | 
            -
                    argv += [redirects] unless RUBY_VERSION < '1.9'
         | 
| 138 | 
            -
                    Kernel.exec(*argv)
         | 
| 139 | 
            -
                  end
         | 
| 140 | 
            -
                end
         | 
| 56 | 
            +
                # The Events object used to output information.
         | 
| 57 | 
            +
                attr_reader :events
         | 
| 141 58 |  | 
| 142 59 | 
             
                # Delegate +log+ to +@events+
         | 
| 143 60 | 
             
                #
         | 
| @@ -289,6 +206,26 @@ module Puma | |
| 289 206 | 
             
                  end
         | 
| 290 207 | 
             
                end
         | 
| 291 208 |  | 
| 209 | 
            +
                def write_state
         | 
| 210 | 
            +
                  write_pid
         | 
| 211 | 
            +
             | 
| 212 | 
            +
                  require 'yaml'
         | 
| 213 | 
            +
             | 
| 214 | 
            +
                  if path = @options[:state]
         | 
| 215 | 
            +
                    state = { "pid" => Process.pid }
         | 
| 216 | 
            +
             | 
| 217 | 
            +
                    cfg = @config.dup
         | 
| 218 | 
            +
             | 
| 219 | 
            +
                    [ :logger, :worker_boot, :on_restart ].each { |o| cfg.options.delete o }
         | 
| 220 | 
            +
             | 
| 221 | 
            +
                    state["config"] = cfg
         | 
| 222 | 
            +
             | 
| 223 | 
            +
                    File.open(path, "w") do |f|
         | 
| 224 | 
            +
                      f.write state.to_yaml
         | 
| 225 | 
            +
                    end
         | 
| 226 | 
            +
                  end
         | 
| 227 | 
            +
                end
         | 
| 228 | 
            +
             | 
| 292 229 | 
             
                # If configured, write the pid of the current process out
         | 
| 293 230 | 
             
                # to a file.
         | 
| 294 231 | 
             
                #
         | 
| @@ -297,6 +234,8 @@ module Puma | |
| 297 234 | 
             
                    File.open(path, "w") do |f|
         | 
| 298 235 | 
             
                      f.puts Process.pid
         | 
| 299 236 | 
             
                    end
         | 
| 237 | 
            +
             | 
| 238 | 
            +
                    at_exit { delete_pidfile }
         | 
| 300 239 | 
             
                  end
         | 
| 301 240 | 
             
                end
         | 
| 302 241 |  | 
| @@ -312,33 +251,9 @@ module Puma | |
| 312 251 | 
             
                  ENV['RACK_ENV'] = env
         | 
| 313 252 | 
             
                end
         | 
| 314 253 |  | 
| 315 | 
            -
                def development?
         | 
| 316 | 
            -
                  @options[:environment] == "development"
         | 
| 317 | 
            -
                end
         | 
| 318 | 
            -
             | 
| 319 254 | 
             
                def delete_pidfile
         | 
| 320 255 | 
             
                  if path = @options[:pidfile]
         | 
| 321 | 
            -
                    File.unlink path
         | 
| 322 | 
            -
                  end
         | 
| 323 | 
            -
                end
         | 
| 324 | 
            -
             | 
| 325 | 
            -
                def write_state
         | 
| 326 | 
            -
                  write_pid
         | 
| 327 | 
            -
             | 
| 328 | 
            -
                  require 'yaml'
         | 
| 329 | 
            -
             | 
| 330 | 
            -
                  if path = @options[:state]
         | 
| 331 | 
            -
                    state = { "pid" => Process.pid }
         | 
| 332 | 
            -
             | 
| 333 | 
            -
                    cfg = @config.dup
         | 
| 334 | 
            -
             | 
| 335 | 
            -
                    [ :logger, :worker_boot, :on_restart ].each { |o| cfg.options.delete o }
         | 
| 336 | 
            -
             | 
| 337 | 
            -
                    state["config"] = cfg
         | 
| 338 | 
            -
             | 
| 339 | 
            -
                    File.open(path, "w") do |f|
         | 
| 340 | 
            -
                      f.write state.to_yaml
         | 
| 341 | 
            -
                    end
         | 
| 256 | 
            +
                    File.unlink path if File.exists? path
         | 
| 342 257 | 
             
                  end
         | 
| 343 258 | 
             
                end
         | 
| 344 259 |  | 
| @@ -357,535 +272,241 @@ module Puma | |
| 357 272 |  | 
| 358 273 | 
             
                  @config.load
         | 
| 359 274 |  | 
| 360 | 
            -
                  if  | 
| 275 | 
            +
                  if clustered?
         | 
| 361 276 | 
             
                    unsupported "worker mode not supported on JRuby and Windows",
         | 
| 362 277 | 
             
                                jruby? || windows?
         | 
| 363 278 | 
             
                  end
         | 
| 279 | 
            +
             | 
| 280 | 
            +
                  if @options[:daemon] and windows?
         | 
| 281 | 
            +
                    unsupported "daemon mode not supported on Windows"
         | 
| 282 | 
            +
                  end
         | 
| 364 283 | 
             
                end
         | 
| 365 284 |  | 
| 366 | 
            -
                def  | 
| 367 | 
            -
                   | 
| 368 | 
            -
                  @status.stop(true) if @status
         | 
| 369 | 
            -
                  server.stop(true)
         | 
| 370 | 
            -
                  delete_pidfile
         | 
| 371 | 
            -
                  log " - Goodbye!"
         | 
| 285 | 
            +
                def clustered?
         | 
| 286 | 
            +
                  @options[:workers] > 0
         | 
| 372 287 | 
             
                end
         | 
| 373 288 |  | 
| 374 | 
            -
                def  | 
| 375 | 
            -
                   | 
| 376 | 
            -
                   | 
| 377 | 
            -
                   | 
| 289 | 
            +
                def graceful_stop
         | 
| 290 | 
            +
                  @control.stop(true) if @control
         | 
| 291 | 
            +
                  @runner.stop_blocked
         | 
| 292 | 
            +
                  log "- Goodbye!"
         | 
| 293 | 
            +
                end
         | 
| 378 294 |  | 
| 379 | 
            -
             | 
| 380 | 
            -
             | 
| 381 | 
            -
             | 
| 382 | 
            -
             | 
| 383 | 
            -
                   | 
| 295 | 
            +
                def generate_restart_data
         | 
| 296 | 
            +
                  # Use the same trick as unicorn, namely favor PWD because
         | 
| 297 | 
            +
                  # it will contain an unresolved symlink, useful for when
         | 
| 298 | 
            +
                  # the pwd is /data/releases/current.
         | 
| 299 | 
            +
                  if dir = ENV['PWD']
         | 
| 300 | 
            +
                    s_env = File.stat(dir)
         | 
| 301 | 
            +
                    s_pwd = File.stat(Dir.pwd)
         | 
| 384 302 |  | 
| 385 | 
            -
             | 
| 386 | 
            -
             | 
| 387 | 
            -
             | 
| 388 | 
            -
                     | 
| 303 | 
            +
                    if s_env.ino == s_pwd.ino and s_env.dev == s_pwd.dev
         | 
| 304 | 
            +
                      @restart_dir = dir
         | 
| 305 | 
            +
                      @options[:worker_directory] = dir
         | 
| 306 | 
            +
                    end
         | 
| 389 307 | 
             
                  end
         | 
| 390 | 
            -
                end
         | 
| 391 308 |  | 
| 392 | 
            -
             | 
| 393 | 
            -
                # for it to finish.
         | 
| 394 | 
            -
                #
         | 
| 395 | 
            -
                def run
         | 
| 396 | 
            -
                  begin
         | 
| 397 | 
            -
                    parse_options
         | 
| 398 | 
            -
                  rescue UnsupportedOption
         | 
| 399 | 
            -
                    exit 1
         | 
| 400 | 
            -
                  end
         | 
| 309 | 
            +
                  @restart_dir ||= Dir.pwd
         | 
| 401 310 |  | 
| 402 | 
            -
                   | 
| 403 | 
            -
                    Dir.chdir dir
         | 
| 404 | 
            -
                  end
         | 
| 311 | 
            +
                  @original_argv = ARGV.dup
         | 
| 405 312 |  | 
| 406 | 
            -
                   | 
| 313 | 
            +
                  if defined? Rubinius::OS_ARGV
         | 
| 314 | 
            +
                    @restart_argv = Rubinius::OS_ARGV
         | 
| 315 | 
            +
                  else
         | 
| 316 | 
            +
                    require 'rubygems'
         | 
| 407 317 |  | 
| 408 | 
            -
             | 
| 409 | 
            -
                     | 
| 410 | 
            -
                     | 
| 411 | 
            -
             | 
| 318 | 
            +
                    # if $0 is a file in the current directory, then restart
         | 
| 319 | 
            +
                    # it the same, otherwise add -S on there because it was
         | 
| 320 | 
            +
                    # picked up in PATH.
         | 
| 321 | 
            +
                    #
         | 
| 322 | 
            +
                    if File.exists?($0)
         | 
| 323 | 
            +
                      arg0 = [Gem.ruby, $0]
         | 
| 324 | 
            +
                    else
         | 
| 325 | 
            +
                      arg0 = [Gem.ruby, "-S", $0]
         | 
| 326 | 
            +
                    end
         | 
| 412 327 |  | 
| 413 | 
            -
             | 
| 328 | 
            +
                    # Detect and reinject -Ilib from the command line
         | 
| 329 | 
            +
                    lib = File.expand_path "lib"
         | 
| 330 | 
            +
                    arg0[1,0] = ["-I", lib] if $:[0] == lib
         | 
| 414 331 |  | 
| 415 | 
            -
             | 
| 416 | 
            -
                    run_cluster
         | 
| 417 | 
            -
                  else
         | 
| 418 | 
            -
                    run_single
         | 
| 332 | 
            +
                    @restart_argv = arg0 + ARGV
         | 
| 419 333 | 
             
                  end
         | 
| 420 334 | 
             
                end
         | 
| 421 335 |  | 
| 422 | 
            -
                def  | 
| 423 | 
            -
                  @options[: | 
| 424 | 
            -
             | 
| 425 | 
            -
             | 
| 426 | 
            -
             | 
| 427 | 
            -
                   | 
| 336 | 
            +
                def restart_args
         | 
| 337 | 
            +
                  if cmd = @options[:restart_cmd]
         | 
| 338 | 
            +
                    cmd.split(' ') + @original_argv
         | 
| 339 | 
            +
                  else
         | 
| 340 | 
            +
                    @restart_argv
         | 
| 341 | 
            +
                  end
         | 
| 428 342 | 
             
                end
         | 
| 429 343 |  | 
| 430 | 
            -
                def  | 
| 431 | 
            -
                   | 
| 432 | 
            -
                   | 
| 433 | 
            -
             | 
| 434 | 
            -
                  log "Puma #{Puma::Const::PUMA_VERSION} starting..."
         | 
| 435 | 
            -
                  log "* Min threads: #{min_t}, max threads: #{max_t}"
         | 
| 436 | 
            -
                  log "* Environment: #{ENV['RACK_ENV']}"
         | 
| 344 | 
            +
                def jruby_daemon_start
         | 
| 345 | 
            +
                  require 'puma/jruby_restart'
         | 
| 346 | 
            +
                  JRubyRestart.daemon_start(@restart_dir, restart_args)
         | 
| 437 347 | 
             
                end
         | 
| 438 348 |  | 
| 439 | 
            -
                def  | 
| 440 | 
            -
                   | 
| 441 | 
            -
             | 
| 442 | 
            -
                  uri = URI.parse str
         | 
| 443 | 
            -
             | 
| 444 | 
            -
                  app = Puma::App::Status.new server, self
         | 
| 445 | 
            -
             | 
| 446 | 
            -
                  if token = @options[:control_auth_token]
         | 
| 447 | 
            -
                    app.auth_token = token unless token.empty? or token == :none
         | 
| 349 | 
            +
                def restart!
         | 
| 350 | 
            +
                  @options[:on_restart].each do |block|
         | 
| 351 | 
            +
                    block.call self
         | 
| 448 352 | 
             
                  end
         | 
| 449 353 |  | 
| 450 | 
            -
                   | 
| 451 | 
            -
             | 
| 452 | 
            -
             | 
| 354 | 
            +
                  if jruby?
         | 
| 355 | 
            +
                    @binder.listeners.each_with_index do |(str,io),i|
         | 
| 356 | 
            +
                      io.close
         | 
| 453 357 |  | 
| 454 | 
            -
             | 
| 455 | 
            -
             | 
| 456 | 
            -
             | 
| 457 | 
            -
             | 
| 458 | 
            -
             | 
| 459 | 
            -
             | 
| 460 | 
            -
                     | 
| 358 | 
            +
                      # We have to unlink a unix socket path that's not being used
         | 
| 359 | 
            +
                      uri = URI.parse str
         | 
| 360 | 
            +
                      if uri.scheme == "unix"
         | 
| 361 | 
            +
                        path = "#{uri.host}#{uri.path}"
         | 
| 362 | 
            +
                        File.unlink path
         | 
| 363 | 
            +
                      end
         | 
| 364 | 
            +
                    end
         | 
| 461 365 |  | 
| 462 | 
            -
                     | 
| 366 | 
            +
                    require 'puma/jruby_restart'
         | 
| 367 | 
            +
                    JRubyRestart.chdir_exec(@restart_dir, restart_args)
         | 
| 463 368 | 
             
                  else
         | 
| 464 | 
            -
                     | 
| 465 | 
            -
             | 
| 369 | 
            +
                    redirects = {:close_others => true}
         | 
| 370 | 
            +
                    @binder.listeners.each_with_index do |(l,io),i|
         | 
| 371 | 
            +
                      ENV["PUMA_INHERIT_#{i}"] = "#{io.to_i}:#{l}"
         | 
| 372 | 
            +
                      redirects[io.to_i] = io.to_i
         | 
| 373 | 
            +
                    end
         | 
| 466 374 |  | 
| 467 | 
            -
             | 
| 468 | 
            -
                  @status = status
         | 
| 469 | 
            -
                end
         | 
| 375 | 
            +
                    argv = restart_args
         | 
| 470 376 |  | 
| 471 | 
            -
             | 
| 472 | 
            -
                  unless @config.app_configured?
         | 
| 473 | 
            -
                    error "No application configured, nothing to run"
         | 
| 474 | 
            -
                    exit 1
         | 
| 475 | 
            -
                  end
         | 
| 377 | 
            +
                    Dir.chdir @restart_dir
         | 
| 476 378 |  | 
| 477 | 
            -
             | 
| 478 | 
            -
             | 
| 479 | 
            -
                    @app = @config.app
         | 
| 480 | 
            -
                  rescue Exception => e
         | 
| 481 | 
            -
                    log "! Unable to load application"
         | 
| 482 | 
            -
                    raise e
         | 
| 379 | 
            +
                    argv += [redirects] unless RUBY_VERSION < '1.9'
         | 
| 380 | 
            +
                    Kernel.exec(*argv)
         | 
| 483 381 | 
             
                  end
         | 
| 484 | 
            -
             | 
| 485 | 
            -
                  @binder.parse @options[:binds], self
         | 
| 486 382 | 
             
                end
         | 
| 487 383 |  | 
| 488 | 
            -
                 | 
| 489 | 
            -
             | 
| 490 | 
            -
             | 
| 491 | 
            -
             | 
| 492 | 
            -
             | 
| 493 | 
            -
             | 
| 494 | 
            -
             | 
| 495 | 
            -
             | 
| 496 | 
            -
                      load_and_bind
         | 
| 497 | 
            -
                    end
         | 
| 498 | 
            -
             | 
| 499 | 
            -
                    already_daemon = JRubyRestart.daemon_init
         | 
| 384 | 
            +
                # Parse the options, load the rackup, start the server and wait
         | 
| 385 | 
            +
                # for it to finish.
         | 
| 386 | 
            +
                #
         | 
| 387 | 
            +
                def run
         | 
| 388 | 
            +
                  begin
         | 
| 389 | 
            +
                    parse_options
         | 
| 390 | 
            +
                  rescue UnsupportedOption
         | 
| 391 | 
            +
                    exit 1
         | 
| 500 392 | 
             
                  end
         | 
| 501 393 |  | 
| 502 | 
            -
                   | 
| 503 | 
            -
             | 
| 504 | 
            -
                   | 
| 505 | 
            -
                    unless already_daemon
         | 
| 506 | 
            -
                      require 'puma/jruby_restart'
         | 
| 394 | 
            +
                  if dir = @options[:directory]
         | 
| 395 | 
            +
                    Dir.chdir dir
         | 
| 396 | 
            +
                  end
         | 
| 507 397 |  | 
| 508 | 
            -
             | 
| 398 | 
            +
                  set_rack_environment
         | 
| 509 399 |  | 
| 510 | 
            -
             | 
| 511 | 
            -
             | 
| 512 | 
            -
             | 
| 513 | 
            -
                      end
         | 
| 400 | 
            +
                  if clustered?
         | 
| 401 | 
            +
                    @events = PidEvents.new STDOUT, STDERR
         | 
| 402 | 
            +
                    @options[:logger] = @events
         | 
| 514 403 |  | 
| 515 | 
            -
             | 
| 516 | 
            -
                      sleep
         | 
| 517 | 
            -
                    end
         | 
| 404 | 
            +
                    @runner = Cluster.new(self)
         | 
| 518 405 | 
             
                  else
         | 
| 519 | 
            -
                     | 
| 520 | 
            -
                    Process.daemon(true) if daemon?
         | 
| 406 | 
            +
                    @runner = Single.new(self)
         | 
| 521 407 | 
             
                  end
         | 
| 522 408 |  | 
| 523 | 
            -
                   | 
| 524 | 
            -
             | 
| 525 | 
            -
                  server = Puma::Server.new @app, @events
         | 
| 526 | 
            -
                  server.binder = @binder
         | 
| 527 | 
            -
                  server.min_threads = @options[:min_threads]
         | 
| 528 | 
            -
                  server.max_threads = @options[:max_threads]
         | 
| 409 | 
            +
                  setup_signals
         | 
| 529 410 |  | 
| 530 | 
            -
                   | 
| 531 | 
            -
                     | 
| 411 | 
            +
                  if cont = @options[:control_url]
         | 
| 412 | 
            +
                    start_control cont
         | 
| 532 413 | 
             
                  end
         | 
| 533 414 |  | 
| 534 | 
            -
                  @ | 
| 415 | 
            +
                  @status = :run
         | 
| 535 416 |  | 
| 536 | 
            -
                   | 
| 537 | 
            -
             | 
| 417 | 
            +
                  @runner.run
         | 
| 418 | 
            +
             | 
| 419 | 
            +
                  case @status
         | 
| 420 | 
            +
                  when :halt
         | 
| 421 | 
            +
                    log "* Stopping immediately!"
         | 
| 422 | 
            +
                  when :run, :stop
         | 
| 423 | 
            +
                    graceful_stop
         | 
| 424 | 
            +
                  when :restart
         | 
| 425 | 
            +
                    log "* Restarting..."
         | 
| 426 | 
            +
                    @control.stop true if @control
         | 
| 427 | 
            +
                    restart!
         | 
| 538 428 | 
             
                  end
         | 
| 429 | 
            +
                end
         | 
| 539 430 |  | 
| 431 | 
            +
                def setup_signals
         | 
| 540 432 | 
             
                  begin
         | 
| 541 433 | 
             
                    Signal.trap "SIGUSR2" do
         | 
| 542 | 
            -
                       | 
| 543 | 
            -
                      server.begin_restart
         | 
| 434 | 
            +
                      restart
         | 
| 544 435 | 
             
                    end
         | 
| 545 436 | 
             
                  rescue Exception
         | 
| 546 | 
            -
                    log "***  | 
| 437 | 
            +
                    log "*** SIGUSR2 not implemented, signal based restart unavailable!"
         | 
| 547 438 | 
             
                  end
         | 
| 548 439 |  | 
| 549 440 | 
             
                  begin
         | 
| 550 441 | 
             
                    Signal.trap "SIGTERM" do
         | 
| 551 | 
            -
                       | 
| 552 | 
            -
                      server.stop false
         | 
| 442 | 
            +
                      stop
         | 
| 553 443 | 
             
                    end
         | 
| 554 444 | 
             
                  rescue Exception
         | 
| 555 | 
            -
                    log "***  | 
| 556 | 
            -
                  end
         | 
| 557 | 
            -
             | 
| 558 | 
            -
                  unless @options[:daemon]
         | 
| 559 | 
            -
                    log "Use Ctrl-C to stop"
         | 
| 445 | 
            +
                    log "*** SIGTERM not implemented, signal based gracefully stopping unavailable!"
         | 
| 560 446 | 
             
                  end
         | 
| 561 447 |  | 
| 562 | 
            -
                  redirect_io
         | 
| 563 | 
            -
             | 
| 564 448 | 
             
                  if jruby?
         | 
| 565 449 | 
             
                    Signal.trap("INT") do
         | 
| 566 | 
            -
                      graceful_stop | 
| 450 | 
            +
                      graceful_stop
         | 
| 567 451 | 
             
                      exit
         | 
| 568 452 | 
             
                    end
         | 
| 569 453 | 
             
                  end
         | 
| 570 | 
            -
             | 
| 571 | 
            -
                  begin
         | 
| 572 | 
            -
                    server.run.join
         | 
| 573 | 
            -
                  rescue Interrupt
         | 
| 574 | 
            -
                    graceful_stop server
         | 
| 575 | 
            -
                  end
         | 
| 576 | 
            -
             | 
| 577 | 
            -
                  if @restart
         | 
| 578 | 
            -
                    log "* Restarting..."
         | 
| 579 | 
            -
                    @status.stop true if @status
         | 
| 580 | 
            -
                    restart!
         | 
| 581 | 
            -
                  end
         | 
| 582 454 | 
             
                end
         | 
| 583 455 |  | 
| 584 | 
            -
                def  | 
| 585 | 
            -
                   | 
| 586 | 
            -
                  Signal.trap "SIGINT", "IGNORE"
         | 
| 587 | 
            -
             | 
| 588 | 
            -
                  @master_read.close
         | 
| 589 | 
            -
                  @suicide_pipe.close
         | 
| 590 | 
            -
             | 
| 591 | 
            -
                  Thread.new do
         | 
| 592 | 
            -
                    IO.select [@check_pipe]
         | 
| 593 | 
            -
                    log "! Detected parent died, dying"
         | 
| 594 | 
            -
                    exit! 1
         | 
| 595 | 
            -
                  end
         | 
| 596 | 
            -
             | 
| 597 | 
            -
                  # Be sure to change the directory again before loading
         | 
| 598 | 
            -
                  # the app. This way we can pick up new code.
         | 
| 599 | 
            -
                  if upgrade
         | 
| 600 | 
            -
                    if dir = @options[:worker_directory]
         | 
| 601 | 
            -
                      log "+ Changing to #{dir}"
         | 
| 602 | 
            -
                      Dir.chdir dir
         | 
| 603 | 
            -
                    end
         | 
| 604 | 
            -
                  end
         | 
| 605 | 
            -
             | 
| 606 | 
            -
                  # Invoke any worker boot hooks so they can get
         | 
| 607 | 
            -
                  # things in shape before booting the app.
         | 
| 608 | 
            -
                  hooks = @options[:worker_boot]
         | 
| 609 | 
            -
                  hooks.each { |h| h.call }
         | 
| 610 | 
            -
             | 
| 611 | 
            -
                  min_t = @options[:min_threads]
         | 
| 612 | 
            -
                  max_t = @options[:max_threads]
         | 
| 613 | 
            -
             | 
| 614 | 
            -
                  # If preload is used, then @app is set to the preloaded
         | 
| 615 | 
            -
                  # application. Otherwise load it now via the config.
         | 
| 616 | 
            -
                  app = @app || @config.app
         | 
| 617 | 
            -
             | 
| 618 | 
            -
                  server = Puma::Server.new app, @events
         | 
| 619 | 
            -
                  server.min_threads = min_t
         | 
| 620 | 
            -
                  server.max_threads = max_t
         | 
| 621 | 
            -
                  server.inherit_binder @binder
         | 
| 456 | 
            +
                def start_control(str)
         | 
| 457 | 
            +
                  require 'puma/app/status'
         | 
| 622 458 |  | 
| 623 | 
            -
                   | 
| 624 | 
            -
                    server.leak_stack_on_error = false
         | 
| 625 | 
            -
                  end
         | 
| 459 | 
            +
                  uri = URI.parse str
         | 
| 626 460 |  | 
| 627 | 
            -
                   | 
| 628 | 
            -
                    server.stop
         | 
| 629 | 
            -
                  end
         | 
| 461 | 
            +
                  app = Puma::App::Status.new self
         | 
| 630 462 |  | 
| 631 | 
            -
                   | 
| 632 | 
            -
                     | 
| 633 | 
            -
                  rescue SystemCallError, IOError
         | 
| 634 | 
            -
                    STDERR.puts "Master seems to have exitted, exitting."
         | 
| 635 | 
            -
                    return
         | 
| 463 | 
            +
                  if token = @options[:control_auth_token]
         | 
| 464 | 
            +
                    app.auth_token = token unless token.empty? or token == :none
         | 
| 636 465 | 
             
                  end
         | 
| 637 466 |  | 
| 638 | 
            -
                   | 
| 467 | 
            +
                  control = Puma::Server.new app, @events
         | 
| 468 | 
            +
                  control.min_threads = 0
         | 
| 469 | 
            +
                  control.max_threads = 1
         | 
| 639 470 |  | 
| 640 | 
            -
             | 
| 641 | 
            -
                   | 
| 642 | 
            -
             | 
| 643 | 
            -
             | 
| 644 | 
            -
             | 
| 645 | 
            -
             | 
| 646 | 
            -
             | 
| 471 | 
            +
                  case uri.scheme
         | 
| 472 | 
            +
                  when "tcp"
         | 
| 473 | 
            +
                    log "* Starting control server on #{str}"
         | 
| 474 | 
            +
                    control.add_tcp_listener uri.host, uri.port
         | 
| 475 | 
            +
                  when "unix"
         | 
| 476 | 
            +
                    log "* Starting control server on #{str}"
         | 
| 477 | 
            +
                    path = "#{uri.host}#{uri.path}"
         | 
| 647 478 |  | 
| 648 | 
            -
             | 
| 649 | 
            -
                    Process.waitall
         | 
| 650 | 
            -
                  rescue Interrupt
         | 
| 651 | 
            -
                    log "! Cancelled waiting for workers"
         | 
| 479 | 
            +
                    control.add_unix_listener path
         | 
| 652 480 | 
             
                  else
         | 
| 653 | 
            -
                     | 
| 654 | 
            -
                  end
         | 
| 655 | 
            -
                end
         | 
| 656 | 
            -
             | 
| 657 | 
            -
                def start_phased_restart
         | 
| 658 | 
            -
                  @phase += 1
         | 
| 659 | 
            -
                  log "- Starting phased worker restart, phase: #{@phase}"
         | 
| 660 | 
            -
                end
         | 
| 661 | 
            -
             | 
| 662 | 
            -
                class Worker
         | 
| 663 | 
            -
                  def initialize(pid, phase)
         | 
| 664 | 
            -
                    @pid = pid
         | 
| 665 | 
            -
                    @phase = phase
         | 
| 666 | 
            -
                    @stage = :started
         | 
| 481 | 
            +
                    error "Invalid control URI: #{str}"
         | 
| 667 482 | 
             
                  end
         | 
| 668 483 |  | 
| 669 | 
            -
                   | 
| 670 | 
            -
             | 
| 671 | 
            -
                  def booted?
         | 
| 672 | 
            -
                    @stage == :booted
         | 
| 673 | 
            -
                  end
         | 
| 674 | 
            -
             | 
| 675 | 
            -
                  def boot!
         | 
| 676 | 
            -
                    @stage = :booted
         | 
| 677 | 
            -
                  end
         | 
| 678 | 
            -
             | 
| 679 | 
            -
                  def term
         | 
| 680 | 
            -
                    begin
         | 
| 681 | 
            -
                      Process.kill "TERM", @pid
         | 
| 682 | 
            -
                    rescue Errno::ESRCH
         | 
| 683 | 
            -
                    end
         | 
| 684 | 
            -
                  end
         | 
| 484 | 
            +
                  control.run
         | 
| 485 | 
            +
                  @control = control
         | 
| 685 486 | 
             
                end
         | 
| 686 487 |  | 
| 687 | 
            -
                def  | 
| 688 | 
            -
                   | 
| 689 | 
            -
             | 
| 690 | 
            -
                  upgrade = (@phased_state == :waiting)
         | 
| 691 | 
            -
             | 
| 692 | 
            -
                  diff.times do
         | 
| 693 | 
            -
                    pid = fork { worker(upgrade) }
         | 
| 694 | 
            -
                    debug "Spawned worker: #{pid}"
         | 
| 695 | 
            -
                    @workers << Worker.new(pid, @phase)
         | 
| 696 | 
            -
                  end
         | 
| 697 | 
            -
             | 
| 698 | 
            -
                  if diff > 0
         | 
| 699 | 
            -
                    @phased_state = :idle
         | 
| 700 | 
            -
                  end
         | 
| 488 | 
            +
                def stop
         | 
| 489 | 
            +
                  @status = :stop
         | 
| 490 | 
            +
                  @runner.stop
         | 
| 701 491 | 
             
                end
         | 
| 702 492 |  | 
| 703 | 
            -
                def  | 
| 704 | 
            -
                  @ | 
| 493 | 
            +
                def restart
         | 
| 494 | 
            +
                  @status = :restart
         | 
| 495 | 
            +
                  @runner.restart
         | 
| 705 496 | 
             
                end
         | 
| 706 497 |  | 
| 707 | 
            -
                def  | 
| 708 | 
            -
                   | 
| 709 | 
            -
             | 
| 710 | 
            -
                    break unless pid
         | 
| 711 | 
            -
             | 
| 712 | 
            -
                    @workers.delete_if { |w| w.pid == pid }
         | 
| 713 | 
            -
                  end
         | 
| 714 | 
            -
             | 
| 715 | 
            -
                  spawn_workers
         | 
| 716 | 
            -
             | 
| 717 | 
            -
                  if @phased_state == :idle && all_workers_booted?
         | 
| 718 | 
            -
                    # If we're running at proper capacity, check to see if
         | 
| 719 | 
            -
                    # we need to phase any workers out (which will restart
         | 
| 720 | 
            -
                    # in the right phase).
         | 
| 721 | 
            -
                    #
         | 
| 722 | 
            -
                    w = @workers.find { |x| x.phase != @phase }
         | 
| 723 | 
            -
             | 
| 724 | 
            -
                    if w
         | 
| 725 | 
            -
                      @phased_state = :waiting
         | 
| 726 | 
            -
                      log "- Stopping #{w.pid} for phased upgrade..."
         | 
| 727 | 
            -
                      w.term
         | 
| 728 | 
            -
                    end
         | 
| 729 | 
            -
                  end
         | 
| 498 | 
            +
                def phased_restart
         | 
| 499 | 
            +
                  return false unless @runner.respond_to? :phased_restart
         | 
| 500 | 
            +
                  @runner.phased_restart
         | 
| 730 501 | 
             
                end
         | 
| 731 502 |  | 
| 732 | 
            -
                def  | 
| 733 | 
            -
                   | 
| 734 | 
            -
                    @wakeup.write "!" unless @wakeup.closed?
         | 
| 735 | 
            -
                  rescue SystemCallError, IOError
         | 
| 736 | 
            -
                  end
         | 
| 503 | 
            +
                def stats
         | 
| 504 | 
            +
                  @runner.stats
         | 
| 737 505 | 
             
                end
         | 
| 738 506 |  | 
| 739 | 
            -
                def  | 
| 740 | 
            -
                   | 
| 741 | 
            -
                   | 
| 742 | 
            -
                  log "* Min threads: #{@options[:min_threads]}, max threads: #{@options[:max_threads]}"
         | 
| 743 | 
            -
                  log "* Environment: #{ENV['RACK_ENV']}"
         | 
| 744 | 
            -
             | 
| 745 | 
            -
                  if @options[:preload_app]
         | 
| 746 | 
            -
                    log "* Preloading application"
         | 
| 747 | 
            -
                    load_and_bind
         | 
| 748 | 
            -
                  else
         | 
| 749 | 
            -
                    log "* Phased restart available"
         | 
| 750 | 
            -
             | 
| 751 | 
            -
                    unless @config.app_configured?
         | 
| 752 | 
            -
                      error "No application configured, nothing to run"
         | 
| 753 | 
            -
                      exit 1
         | 
| 754 | 
            -
                    end
         | 
| 755 | 
            -
             | 
| 756 | 
            -
                    @binder.parse @options[:binds], self
         | 
| 757 | 
            -
                  end
         | 
| 758 | 
            -
             | 
| 759 | 
            -
                  read, @wakeup = Puma::Util.pipe
         | 
| 760 | 
            -
             | 
| 761 | 
            -
                  Signal.trap "SIGCHLD" do
         | 
| 762 | 
            -
                    wakeup!
         | 
| 763 | 
            -
                  end
         | 
| 764 | 
            -
             | 
| 765 | 
            -
                  stop = false
         | 
| 766 | 
            -
             | 
| 767 | 
            -
                  begin
         | 
| 768 | 
            -
                    Signal.trap "SIGUSR2" do
         | 
| 769 | 
            -
                      @restart = true
         | 
| 770 | 
            -
                      stop = true
         | 
| 771 | 
            -
                      wakeup!
         | 
| 772 | 
            -
                    end
         | 
| 773 | 
            -
                  rescue Exception
         | 
| 774 | 
            -
                  end
         | 
| 775 | 
            -
             | 
| 776 | 
            -
                  master_pid = Process.pid
         | 
| 777 | 
            -
             | 
| 778 | 
            -
                  begin
         | 
| 779 | 
            -
                    Signal.trap "SIGTERM" do
         | 
| 780 | 
            -
                      # The worker installs their own SIGTERM when booted.
         | 
| 781 | 
            -
                      # Until then, this is run by the worker and the worker
         | 
| 782 | 
            -
                      # should just exit if they get it.
         | 
| 783 | 
            -
                      if Process.pid != master_pid
         | 
| 784 | 
            -
                        log "Early termination of worker"
         | 
| 785 | 
            -
                        exit! 0
         | 
| 786 | 
            -
                      else
         | 
| 787 | 
            -
                        stop = true
         | 
| 788 | 
            -
                        wakeup!
         | 
| 789 | 
            -
                      end
         | 
| 790 | 
            -
                    end
         | 
| 791 | 
            -
                  rescue Exception
         | 
| 792 | 
            -
                  end
         | 
| 793 | 
            -
             | 
| 794 | 
            -
                  phased_restart = false
         | 
| 795 | 
            -
             | 
| 796 | 
            -
                  begin
         | 
| 797 | 
            -
                    if @options[:preload_app]
         | 
| 798 | 
            -
                      Signal.trap "SIGUSR1" do
         | 
| 799 | 
            -
                        log "App preloaded, phased restart unavailable"
         | 
| 800 | 
            -
                      end
         | 
| 801 | 
            -
                    else
         | 
| 802 | 
            -
                      Signal.trap "SIGUSR1" do
         | 
| 803 | 
            -
                        phased_restart = true
         | 
| 804 | 
            -
                        @wakeup << "!"
         | 
| 805 | 
            -
                        wakeup!
         | 
| 806 | 
            -
                      end
         | 
| 807 | 
            -
                    end
         | 
| 808 | 
            -
                  rescue Exception
         | 
| 809 | 
            -
                  end
         | 
| 810 | 
            -
             | 
| 811 | 
            -
                  # Used by the workers to detect if the master process dies.
         | 
| 812 | 
            -
                  # If select says that @check_pipe is ready, it's because the
         | 
| 813 | 
            -
                  # master has exited and @suicide_pipe has been automatically
         | 
| 814 | 
            -
                  # closed.
         | 
| 815 | 
            -
                  #
         | 
| 816 | 
            -
                  @check_pipe, @suicide_pipe = Puma::Util.pipe
         | 
| 817 | 
            -
             | 
| 818 | 
            -
                  if @options[:daemon]
         | 
| 819 | 
            -
                    Process.daemon(true)
         | 
| 820 | 
            -
                  else
         | 
| 821 | 
            -
                    log "Use Ctrl-C to stop"
         | 
| 822 | 
            -
                  end
         | 
| 823 | 
            -
             | 
| 824 | 
            -
                  @master_pid = Process.pid
         | 
| 825 | 
            -
             | 
| 826 | 
            -
                  redirect_io
         | 
| 827 | 
            -
             | 
| 828 | 
            -
                  write_state
         | 
| 829 | 
            -
             | 
| 830 | 
            -
                  @master_read, @worker_write = read, @wakeup
         | 
| 831 | 
            -
                  spawn_workers
         | 
| 832 | 
            -
             | 
| 833 | 
            -
                  Signal.trap "SIGINT" do
         | 
| 834 | 
            -
                    stop = true
         | 
| 835 | 
            -
                    wakeup!
         | 
| 836 | 
            -
                  end
         | 
| 837 | 
            -
             | 
| 838 | 
            -
                  begin
         | 
| 839 | 
            -
                    while !stop
         | 
| 840 | 
            -
                      begin
         | 
| 841 | 
            -
                        res = IO.select([read], nil, nil, 5)
         | 
| 842 | 
            -
             | 
| 843 | 
            -
                        if res
         | 
| 844 | 
            -
                          req = read.read_nonblock(1)
         | 
| 845 | 
            -
             | 
| 846 | 
            -
                          if req == "b"
         | 
| 847 | 
            -
                            pid = read.gets.to_i
         | 
| 848 | 
            -
                            w = @workers.find { |x| x.pid == pid }
         | 
| 849 | 
            -
                            if w
         | 
| 850 | 
            -
                              w.boot!
         | 
| 851 | 
            -
                              log "- Worker #{pid} booted, phase: #{w.phase}"
         | 
| 852 | 
            -
                            else
         | 
| 853 | 
            -
                              log "! Out-of-sync worker list, no #{pid} worker"
         | 
| 854 | 
            -
                            end
         | 
| 855 | 
            -
                          end
         | 
| 856 | 
            -
                        end
         | 
| 857 | 
            -
             | 
| 858 | 
            -
                        check_workers
         | 
| 859 | 
            -
             | 
| 860 | 
            -
                        if phased_restart
         | 
| 861 | 
            -
                          start_phased_restart
         | 
| 862 | 
            -
                          phased_restart = false
         | 
| 863 | 
            -
                        end
         | 
| 864 | 
            -
             | 
| 865 | 
            -
                      rescue Interrupt
         | 
| 866 | 
            -
                        stop = true
         | 
| 867 | 
            -
                      end
         | 
| 868 | 
            -
                    end
         | 
| 869 | 
            -
             | 
| 870 | 
            -
                    stop_workers
         | 
| 871 | 
            -
                  ensure
         | 
| 872 | 
            -
                    delete_pidfile
         | 
| 873 | 
            -
                    @check_pipe.close
         | 
| 874 | 
            -
                    @suicide_pipe.close
         | 
| 875 | 
            -
                    read.close
         | 
| 876 | 
            -
                    @wakeup.close
         | 
| 877 | 
            -
                  end
         | 
| 878 | 
            -
             | 
| 879 | 
            -
                  if @restart
         | 
| 880 | 
            -
                    log "* Restarting..."
         | 
| 881 | 
            -
                    restart!
         | 
| 882 | 
            -
                  end
         | 
| 883 | 
            -
                end
         | 
| 884 | 
            -
             | 
| 885 | 
            -
                def stop
         | 
| 886 | 
            -
                  @status.stop(true) if @status
         | 
| 887 | 
            -
                  @server.stop(true) if @server
         | 
| 888 | 
            -
                  delete_pidfile
         | 
| 507 | 
            +
                def halt
         | 
| 508 | 
            +
                  @status = :halt
         | 
| 509 | 
            +
                  @runner.halt
         | 
| 889 510 | 
             
                end
         | 
| 890 511 | 
             
              end
         | 
| 891 512 | 
             
            end
         |