puma 7.1.0-java → 8.0.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.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +116 -0
  3. data/README.md +18 -11
  4. data/docs/5.0-Upgrade.md +98 -0
  5. data/docs/6.0-Upgrade.md +56 -0
  6. data/docs/7.0-Upgrade.md +52 -0
  7. data/docs/8.0-Upgrade.md +100 -0
  8. data/docs/deployment.md +58 -23
  9. data/docs/grpc.md +62 -0
  10. data/docs/images/favicon.svg +1 -0
  11. data/docs/images/running-puma.svg +1 -0
  12. data/docs/images/standard-logo.svg +1 -0
  13. data/docs/jungle/README.md +1 -1
  14. data/docs/kubernetes.md +3 -10
  15. data/docs/plugins.md +2 -2
  16. data/docs/signals.md +10 -10
  17. data/docs/stats.md +1 -1
  18. data/docs/systemd.md +3 -3
  19. data/ext/puma_http11/http11_parser.java.rl +51 -65
  20. data/ext/puma_http11/org/jruby/puma/EnvKey.java +241 -0
  21. data/ext/puma_http11/org/jruby/puma/Http11.java +168 -104
  22. data/ext/puma_http11/org/jruby/puma/Http11Parser.java +71 -85
  23. data/ext/puma_http11/puma_http11.c +101 -109
  24. data/lib/puma/app/status.rb +10 -2
  25. data/lib/puma/cli.rb +1 -1
  26. data/lib/puma/client.rb +90 -66
  27. data/lib/puma/client_env.rb +171 -0
  28. data/lib/puma/cluster/worker.rb +10 -9
  29. data/lib/puma/cluster.rb +3 -4
  30. data/lib/puma/configuration.rb +85 -16
  31. data/lib/puma/const.rb +2 -2
  32. data/lib/puma/control_cli.rb +1 -1
  33. data/lib/puma/detect.rb +11 -0
  34. data/lib/puma/dsl.rb +90 -14
  35. data/lib/puma/launcher.rb +7 -7
  36. data/lib/puma/puma_http11.jar +0 -0
  37. data/lib/puma/reactor.rb +3 -12
  38. data/lib/puma/{request.rb → response.rb} +25 -194
  39. data/lib/puma/runner.rb +1 -1
  40. data/lib/puma/server.rb +72 -37
  41. data/lib/puma/server_plugin_control.rb +32 -0
  42. data/lib/puma/single.rb +2 -2
  43. data/lib/puma/thread_pool.rb +129 -23
  44. data/lib/rack/handler/puma.rb +1 -1
  45. data/tools/Dockerfile +13 -5
  46. metadata +17 -7
  47. data/ext/puma_http11/ext_help.h +0 -15
@@ -1,5 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'socket'
4
+ require 'uri'
5
+
3
6
  require_relative 'plugin'
4
7
  require_relative 'const'
5
8
  require_relative 'dsl'
@@ -131,14 +134,16 @@ module Puma
131
134
 
132
135
  DEFAULTS = {
133
136
  auto_trim_time: 30,
134
- binds: ['tcp://0.0.0.0:9292'.freeze],
135
- fiber_per_request: !!ENV.fetch("PUMA_FIBER_PER_REQUEST", false),
137
+ binds: ['tcp://[::]:9292'.freeze],
136
138
  debug: false,
137
- enable_keep_alives: true,
138
139
  early_hints: nil,
140
+ enable_keep_alives: true,
139
141
  environment: 'development'.freeze,
142
+ fiber_per_request: !!ENV.fetch("PUMA_FIBER_PER_REQUEST", false),
140
143
  # Number of seconds to wait until we get the first data for the request.
141
144
  first_data_timeout: 30,
145
+ force_shutdown_after: -1,
146
+ http_content_length_limit: nil,
142
147
  # Number of seconds to wait until the next request before shutting down.
143
148
  idle_timeout: nil,
144
149
  io_selector_backend: :auto,
@@ -147,6 +152,7 @@ module Puma
147
152
  # Limits how many requests a keep alive connection can make.
148
153
  # The connection will be closed after it reaches `max_keep_alive`
149
154
  # requests.
155
+ max_io_threads: 0,
150
156
  max_keep_alive: 999,
151
157
  max_threads: Puma.mri? ? 5 : 16,
152
158
  min_threads: 0,
@@ -161,10 +167,10 @@ module Puma
161
167
  raise_exception_on_sigterm: true,
162
168
  reaping_time: 1,
163
169
  remote_address: :socket,
164
- silence_single_worker_warning: false,
165
170
  silence_fork_callback_warning: false,
171
+ silence_single_worker_warning: false,
166
172
  tag: File.basename(Dir.getwd),
167
- tcp_host: '0.0.0.0'.freeze,
173
+ tcp_host: '::'.freeze,
168
174
  tcp_port: 9292,
169
175
  wait_for_less_busy_worker: 0.005,
170
176
  worker_boot_timeout: 60,
@@ -173,11 +179,10 @@ module Puma
173
179
  worker_shutdown_timeout: 30,
174
180
  worker_timeout: 60,
175
181
  workers: 0,
176
- http_content_length_limit: nil
177
182
  }
178
183
 
179
184
  def initialize(user_options={}, default_options = {}, env = ENV, &block)
180
- default_options = self.puma_default_options(env).merge(default_options)
185
+ default_options = self.puma_default_options(env).merge(events: Events.new).merge(default_options)
181
186
 
182
187
  @_options = UserFileDefaultOptions.new(user_options, default_options)
183
188
  @plugins = PluginLoader.new
@@ -197,7 +202,7 @@ module Puma
197
202
  @clamped = false
198
203
  end
199
204
 
200
- attr_reader :plugins, :events, :hooks
205
+ attr_reader :plugins, :events, :hooks, :_options
201
206
 
202
207
  def options
203
208
  raise NotClampedError, "ensure clamp is called before accessing options" unless @clamped
@@ -230,6 +235,8 @@ module Puma
230
235
 
231
236
  def puma_default_options(env = ENV)
232
237
  defaults = DEFAULTS.dup
238
+ defaults[:tcp_host] = self.class.default_tcp_host
239
+ defaults[:binds] = [self.class.default_tcp_bind]
233
240
  puma_options_from_env(env).each { |k,v| defaults[k] = v if v }
234
241
  defaults
235
242
  end
@@ -238,18 +245,14 @@ module Puma
238
245
  min = env['PUMA_MIN_THREADS'] || env['MIN_THREADS']
239
246
  max = env['PUMA_MAX_THREADS'] || env['MAX_THREADS']
240
247
  persistent_timeout = env['PUMA_PERSISTENT_TIMEOUT']
241
- workers = if env['WEB_CONCURRENCY'] == 'auto'
242
- require_processor_counter
243
- ::Concurrent.available_processor_count
244
- else
245
- env['WEB_CONCURRENCY']
246
- end
248
+ workers_env = env['WEB_CONCURRENCY']
249
+ workers = workers_env && workers_env.strip != "" ? parse_workers(workers_env.strip) : nil
247
250
 
248
251
  {
249
252
  min_threads: min && min != "" && Integer(min),
250
253
  max_threads: max && max != "" && Integer(max),
251
254
  persistent_timeout: persistent_timeout && persistent_timeout != "" && Integer(persistent_timeout),
252
- workers: workers && workers != "" && Integer(workers),
255
+ workers: workers,
253
256
  environment: env['APP_ENV'] || env['RACK_ENV'] || env['RAILS_ENV'],
254
257
  }
255
258
  end
@@ -281,8 +284,10 @@ module Puma
281
284
  # This also calls load if it hasn't been called yet.
282
285
  def clamp
283
286
  load unless @loaded
287
+ run_mode_hooks
284
288
  set_conditional_default_options
285
289
  @_options.finalize_values
290
+ rewrite_unavailable_ipv6_binds!
286
291
  @clamped = true
287
292
  warn_hooks
288
293
  options
@@ -361,6 +366,23 @@ module Puma
361
366
  options.final_options
362
367
  end
363
368
 
369
+ def self.default_tcp_host
370
+ ipv6_interface_available? ? Const::UNSPECIFIED_IPV6 : Const::UNSPECIFIED_IPV4
371
+ end
372
+
373
+ def self.default_tcp_bind(port = DEFAULTS[:tcp_port])
374
+ URI::Generic.build(scheme: 'tcp', host: default_tcp_host, port: Integer(port)).to_s
375
+ end
376
+
377
+ def self.ipv6_interface_available?
378
+ Socket.getifaddrs.any? do |ifaddr|
379
+ addr = ifaddr.addr
380
+ addr&.ipv6? && !addr&.ipv6_loopback?
381
+ end
382
+ rescue StandardError
383
+ false
384
+ end
385
+
364
386
  def self.temp_path
365
387
  require 'tmpdir'
366
388
 
@@ -376,16 +398,52 @@ module Puma
376
398
 
377
399
  private
378
400
 
401
+ def rewrite_unavailable_ipv6_binds!
402
+ return if self.class.ipv6_interface_available?
403
+
404
+ tried_ipv6_bind = false
405
+ bind_schemes = ['tcp', 'ssl']
406
+
407
+ @_options[:binds] = Array(@_options[:binds]).map do |bind|
408
+ uri = URI.parse(bind)
409
+ next bind unless bind_schemes.include?(uri.scheme)
410
+
411
+ host = uri.host&.delete_prefix('[')&.delete_suffix(']')
412
+ next bind unless host&.include?(':')
413
+
414
+ tried_ipv6_bind = true
415
+ next bind unless host == Const::UNSPECIFIED_IPV6
416
+
417
+ uri.host = Const::UNSPECIFIED_IPV4
418
+ uri.to_s
419
+ rescue URI::InvalidURIError
420
+ bind
421
+ end
422
+
423
+ warn "WARNING: IPv6 bind requested but no non-loopback IPv6 interface was detected" if tried_ipv6_bind
424
+ end
425
+
379
426
  def require_processor_counter
380
427
  require 'concurrent/utility/processor_counter'
381
428
  rescue LoadError
382
429
  warn <<~MESSAGE
383
- WEB_CONCURRENCY=auto requires the "concurrent-ruby" gem to be installed.
430
+ WEB_CONCURRENCY=auto or workers(:auto) requires the "concurrent-ruby" gem to be installed.
384
431
  Please add "concurrent-ruby" to your Gemfile.
385
432
  MESSAGE
386
433
  raise
387
434
  end
388
435
 
436
+ def parse_workers(value)
437
+ if value == :auto || value == 'auto'
438
+ require_processor_counter
439
+ Integer(::Concurrent.available_processor_count)
440
+ else
441
+ Integer(value)
442
+ end
443
+ rescue ArgumentError, TypeError
444
+ raise ArgumentError, "workers must be an Integer or :auto"
445
+ end
446
+
389
447
  # Load and use the normal Rack builder if we can, otherwise
390
448
  # fallback to our minimal version.
391
449
  def rack_builder
@@ -426,6 +484,17 @@ module Puma
426
484
  rack_app
427
485
  end
428
486
 
487
+ def run_mode_hooks
488
+ workers_before = @_options[:workers]
489
+ key = workers_before > 0 ? :cluster : :single
490
+
491
+ @_options.all_of(key).each(&:call)
492
+
493
+ unless @_options[:workers] == workers_before
494
+ raise "cannot change the number of workers inside a #{key} configuration hook"
495
+ end
496
+ end
497
+
429
498
  def set_conditional_default_options
430
499
  @_options.default_options[:preload_app] = !@_options[:prune_bundler] &&
431
500
  (@_options[:workers] > 1) && Puma.forkable?
data/lib/puma/const.rb CHANGED
@@ -100,8 +100,8 @@ module Puma
100
100
  # too taxing on performance.
101
101
  module Const
102
102
 
103
- PUMA_VERSION = VERSION = "7.1.0"
104
- CODE_NAME = "Neon Witch"
103
+ PUMA_VERSION = VERSION = "8.0.0"
104
+ CODE_NAME = "Into the Arena"
105
105
 
106
106
  PUMA_SERVER_STRING = ["puma", PUMA_VERSION, CODE_NAME].join(" ").freeze
107
107
 
@@ -16,7 +16,7 @@ module Puma
16
16
  'gc' => nil,
17
17
  'gc-stats' => nil,
18
18
  'halt' => 'SIGQUIT',
19
- 'info' => 'SIGINFO',
19
+ 'info' => Puma.backtrace_signal,
20
20
  'phased-restart' => 'SIGUSR1',
21
21
  'refork' => 'SIGURG',
22
22
  'reload-worker-directory' => nil,
data/lib/puma/detect.rb CHANGED
@@ -35,6 +35,13 @@ module Puma
35
35
  IS_WINDOWS
36
36
  end
37
37
 
38
+ BACKTRACE_SIGNAL =
39
+ if Signal.list.key?("INFO")
40
+ "SIGINFO"
41
+ elsif Signal.list.key?("PWR")
42
+ "SIGPWR"
43
+ end
44
+
38
45
  # @version 5.0.0
39
46
  def self.mri?
40
47
  IS_MRI
@@ -44,4 +51,8 @@ module Puma
44
51
  def self.forkable?
45
52
  HAS_FORK
46
53
  end
54
+
55
+ def self.backtrace_signal
56
+ BACKTRACE_SIGNAL
57
+ end
47
58
  end
data/lib/puma/dsl.rb CHANGED
@@ -155,7 +155,7 @@ module Puma
155
155
  end
156
156
 
157
157
  def default_host
158
- @options[:default_host] || Configuration::DEFAULTS[:tcp_host]
158
+ @options[:default_host] || Configuration.default_tcp_host
159
159
  end
160
160
 
161
161
  def inject(&blk)
@@ -216,6 +216,8 @@ module Puma
216
216
  # activate_control_app 'unix:///var/run/pumactl.sock', { auth_token: '12345' }
217
217
  # @example
218
218
  # activate_control_app 'unix:///var/run/pumactl.sock', { no_token: true }
219
+ # @example
220
+ # activate_control_app 'unix:///var/run/pumactl.sock', { no_token: true, data_only: true}
219
221
  #
220
222
  def activate_control_app(url="auto", opts={})
221
223
  if url == "auto"
@@ -240,6 +242,7 @@ module Puma
240
242
 
241
243
  @options[:control_auth_token] = auth_token
242
244
  @options[:control_url_umask] = opts[:umask] if opts[:umask]
245
+ @options[:control_data_only] = opts[:data_only] if opts[:data_only]
243
246
  end
244
247
 
245
248
  # Load additional configuration from a file.
@@ -257,7 +260,8 @@ module Puma
257
260
  # accepted protocols. Multiple urls can be bound to, calling +bind+ does
258
261
  # not overwrite previous bindings.
259
262
  #
260
- # The default is "tcp://0.0.0.0:9292".
263
+ # The default is "tcp://[::]:9292" when IPv6 interfaces are available,
264
+ # otherwise "tcp://0.0.0.0:9292".
261
265
  #
262
266
  # You can use query parameters within the url to specify options:
263
267
  #
@@ -276,7 +280,7 @@ module Puma
276
280
  # @example SSL cert for mutual TLS (mTLS)
277
281
  # bind 'ssl://127.0.0.1:9292?key=key.key&cert=cert.pem&ca=ca.pem&verify_mode=force_peer'
278
282
  # @example Disable optimization for low latency
279
- # bind 'tcp://0.0.0.0:9292?low_latency=false'
283
+ # bind 'tcp://[::]:9292?low_latency=false'
280
284
  # @example Socket permissions
281
285
  # bind 'unix:///var/run/puma.sock?umask=0111'
282
286
  #
@@ -346,7 +350,7 @@ module Puma
346
350
 
347
351
  # Define how long persistent connections can be idle before Puma closes them.
348
352
  #
349
- # The default is 20 seconds.
353
+ # The default is 65 seconds.
350
354
  #
351
355
  # @example
352
356
  # persistent_timeout 30
@@ -592,6 +596,29 @@ module Puma
592
596
  @options[:max_threads] = max
593
597
  end
594
598
 
599
+ # Configure the max number of IO threads.
600
+ #
601
+ # When request handlers know the current requests will no longer use a significant amount
602
+ # of CPU, they can mark the current request as IO bound using <tt>env["puma.mark_as_io_bound"]</tt>.
603
+ #
604
+ # Threads marked as IO bound are allowed to go over the max thread limit.
605
+ #
606
+ # @example
607
+ # threads 5
608
+ # max_io_threads 5
609
+ #
610
+ # The above example allows for 5 regular threads and 5 IO threads to process requests concurrently.
611
+ # Any IO thread over the limit is counted as a regular thread, hence the above configuration also
612
+ # allows for 3 regular threads and 7 IO threads for example.
613
+ def max_io_threads(max)
614
+ max = Integer(max)
615
+ if max < 0
616
+ raise "The maximum number of IO threads (#{max}) must be a positive number"
617
+ end
618
+
619
+ @options[:max_io_threads] = max
620
+ end
621
+
595
622
  # Instead of using +bind+ and manually constructing a URI like:
596
623
  #
597
624
  # bind 'ssl://127.0.0.1:9292?key=key_path&cert=cert_path'
@@ -666,21 +693,27 @@ module Puma
666
693
  @options[:state_permission] = permission
667
694
  end
668
695
 
669
- # How many worker processes to run. Typically this is set to
670
- # the number of available cores.
696
+ # How many worker processes to run. Typically this is set to the number of
697
+ # available cores.
671
698
  #
672
699
  # The default is the value of the environment variable +WEB_CONCURRENCY+ if
673
- # set, otherwise 0.
700
+ # set, otherwise 0. Passing +:auto+ will set the value to
701
+ # +Concurrent.available_processor_count+ (requires the concurrent-ruby gem).
702
+ # On some platforms (e.g. under CPU quotas) this may be fractional, and Puma
703
+ # will round down. If it rounds down to 0, Puma will run in single mode and
704
+ # cluster-only hooks like +before_worker_boot+ will not execute.
705
+ # If you rely on cluster-only hooks, set an explicit worker count.
674
706
  #
675
- # @note Cluster mode only.
707
+ # A value of 0 or nil means run in single mode.
676
708
  #
677
709
  # @example
678
710
  # workers 2
711
+ # workers :auto
679
712
  #
680
713
  # @see Puma::Cluster
681
714
  #
682
715
  def workers(count)
683
- @options[:workers] = count.to_i
716
+ @options[:workers] = count.nil? ? 0 : @config.send(:parse_workers, count)
684
717
  end
685
718
 
686
719
  # Disable warning message when running in cluster mode with a single worker.
@@ -718,6 +751,44 @@ module Puma
718
751
  @options[:silence_fork_callback_warning] = true
719
752
  end
720
753
 
754
+ # Code to run only in single mode.
755
+ # Runs after all config files are loaded.
756
+ #
757
+ # This can be called multiple times.
758
+ #
759
+ # @note Single mode only.
760
+ #
761
+ # @example
762
+ # single do
763
+ # silence_fork_callback_warning
764
+ # end
765
+ #
766
+ def single(&block)
767
+ raise ArgumentError, "A block must be provided to `single`" unless block
768
+
769
+ @options[:single] ||= []
770
+ @options[:single] << block
771
+ end
772
+
773
+ # Code to run only in cluster mode.
774
+ # Runs after all config files are loaded.
775
+ #
776
+ # This can be called multiple times.
777
+ #
778
+ # @note Cluster mode only.
779
+ #
780
+ # @example
781
+ # cluster do
782
+ # prune_bundler
783
+ # end
784
+ #
785
+ def cluster(&block)
786
+ raise ArgumentError, "A block must be provided to `cluster`" unless block
787
+
788
+ @options[:cluster] ||= []
789
+ @options[:cluster] << block
790
+ end
791
+
721
792
  # Code to run immediately before master process
722
793
  # forks workers (once on boot). These hooks can block if necessary
723
794
  # to wait for background operations unknown to Puma to finish before
@@ -995,6 +1066,7 @@ module Puma
995
1066
  # The default is +true+ if your app uses more than 1 worker.
996
1067
  #
997
1068
  # @note Cluster mode only.
1069
+ # @note When using `fork_worker`, this only applies to worker 0.
998
1070
  #
999
1071
  # @example
1000
1072
  # preload_app!
@@ -1030,6 +1102,7 @@ module Puma
1030
1102
  # new Bundler context and thus can float around as the release
1031
1103
  # dictates.
1032
1104
  #
1105
+ # @note Cluster mode only.
1033
1106
  # @note This is incompatible with +preload_app!+.
1034
1107
  # @note This is only supported for RubyGems 2.2+
1035
1108
  #
@@ -1135,7 +1208,7 @@ module Puma
1135
1208
 
1136
1209
  # Change the default worker timeout for booting.
1137
1210
  #
1138
- # The default is the value of `worker_timeout`.
1211
+ # The default is 60 seconds.
1139
1212
  #
1140
1213
  # @note Cluster mode only.
1141
1214
  #
@@ -1217,11 +1290,14 @@ module Puma
1217
1290
  # threads will be written to $stdout. This can help figure
1218
1291
  # out why shutdown is hanging.
1219
1292
  #
1220
- def shutdown_debug(val=true)
1221
- @options[:shutdown_debug] = val
1293
+ # If `on_force` is true, the backtraces will be written only
1294
+ # when the shutdown is forced i.e. not graceful.
1295
+ #
1296
+ # @see force_shutdown_after
1297
+ def shutdown_debug(val = true, on_force: false)
1298
+ @options[:shutdown_debug] = val && on_force ? :on_force : val
1222
1299
  end
1223
1300
 
1224
-
1225
1301
  # Maximum delay of worker accept loop.
1226
1302
  #
1227
1303
  # Attempts to route traffic to less-busy workers by causing a busy worker to delay
@@ -1378,7 +1454,7 @@ module Puma
1378
1454
  #
1379
1455
  # The default is +:auto+.
1380
1456
  #
1381
- # @see https://github.com/socketry/nio4r/blob/master/lib/nio/selector.rb
1457
+ # @see https://github.com/socketry/nio4r/blob/main/lib/nio/selector.rb
1382
1458
  #
1383
1459
  def io_selector_backend(backend)
1384
1460
  @options[:io_selector_backend] = backend.to_sym
data/lib/puma/launcher.rb CHANGED
@@ -42,9 +42,11 @@ module Puma
42
42
  # end
43
43
  # Puma::Launcher.new(conf, log_writer: Puma::LogWriter.stdio).run
44
44
  def initialize(conf, launcher_args={})
45
- ## Minimal initialization for a potential early restart (e.g. when pruning bundle)
45
+ ## Minimal initialization before potential early restart (e.g. from bundle pruning)
46
46
 
47
47
  @config = conf
48
+ # Advertise the CLI Configuration before config files are loaded
49
+ Puma.cli_config = @config if defined?(Puma.cli_config)
48
50
  @config.clamp
49
51
 
50
52
  @options = @config.options
@@ -70,8 +72,7 @@ module Puma
70
72
 
71
73
  env = launcher_args.delete(:env) || ENV
72
74
 
73
- # Advertise the Configuration
74
- Puma.cli_config = @config if defined?(Puma.cli_config)
75
+ # Log after prune_bundler! to avoid duplicate logging if a restart occurs
75
76
  log_config if env['PUMA_LOG_CONFIG']
76
77
 
77
78
  @binder = Binder.new(@log_writer, @options)
@@ -475,8 +476,8 @@ module Puma
475
476
  end
476
477
 
477
478
  begin
478
- unless Puma.jruby? # INFO in use by JVM already
479
- Signal.trap "SIGINFO" do
479
+ if Puma.backtrace_signal
480
+ Signal.trap Puma.backtrace_signal do
480
481
  thread_status do |name, backtrace|
481
482
  @log_writer.log(name)
482
483
  @log_writer.log(backtrace.map { |bt| " #{bt}" })
@@ -484,8 +485,7 @@ module Puma
484
485
  end
485
486
  end
486
487
  rescue Exception
487
- # Not going to log this one, as SIGINFO is *BSD only and would be pretty annoying
488
- # to see this constantly on Linux.
488
+ log "*** SIGINFO/SIGPWR not implemented, signal based backtrace unavailable!"
489
489
  end
490
490
  end
491
491
 
Binary file
data/lib/puma/reactor.rb CHANGED
@@ -75,15 +75,12 @@ module Puma
75
75
  private
76
76
 
77
77
  def select_loop
78
- close_selector = true
79
78
  begin
80
79
  until @input.closed? && @input.empty?
81
80
  # Wakeup any registered object that receives incoming data.
82
81
  # Block until the earliest timeout or Selector#wakeup is called.
83
82
  timeout = (earliest = @timeouts.first) && earliest.timeout
84
- monitor_wake_up = false
85
83
  @selector.select(timeout) do |monitor|
86
- monitor_wake_up = true
87
84
  wakeup!(monitor.value)
88
85
  end
89
86
 
@@ -103,18 +100,12 @@ module Puma
103
100
  STDERR.puts "Error in reactor loop escaped: #{e.message} (#{e.class})"
104
101
  STDERR.puts e.backtrace
105
102
 
106
- # NoMethodError may be rarely raised when calling @selector.select, which
107
- # is odd. Regardless, it may continue for thousands of calls if retried.
108
- # Also, when it raises, @selector.close also raises an error.
109
- if !monitor_wake_up && NoMethodError === e
110
- close_selector = false
111
- else
112
- retry
113
- end
103
+ retry
114
104
  end
105
+
115
106
  # Wakeup all remaining objects on shutdown.
116
107
  @timeouts.each(&@block)
117
- @selector.close if close_selector
108
+ @selector.close
118
109
  end
119
110
 
120
111
  # Start monitoring the object.