puma 6.6.1 → 7.0.3

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.
@@ -25,6 +25,8 @@ module Puma
25
25
  # up its work before leaving the thread to die on the vine.
26
26
  SHUTDOWN_GRACE_TIME = 5 # seconds
27
27
 
28
+ attr_reader :out_of_band_running
29
+
28
30
  # Maintain a minimum of +min+ and maximum of +max+ threads
29
31
  # in the pool.
30
32
  #
@@ -35,9 +37,9 @@ module Puma
35
37
  @not_empty = ConditionVariable.new
36
38
  @not_full = ConditionVariable.new
37
39
  @mutex = Mutex.new
40
+ @todo = Queue.new
38
41
 
39
- @todo = []
40
-
42
+ @backlog_max = 0
41
43
  @spawned = 0
42
44
  @waiting = 0
43
45
 
@@ -50,7 +52,8 @@ module Puma
50
52
  @shutdown_grace_time = Float(options[:pool_shutdown_grace_time] || SHUTDOWN_GRACE_TIME)
51
53
  @block = block
52
54
  @out_of_band = options[:out_of_band]
53
- @clean_thread_locals = options[:clean_thread_locals]
55
+ @out_of_band_running = false
56
+ @out_of_band_condvar = ConditionVariable.new
54
57
  @before_thread_start = options[:before_thread_start]
55
58
  @before_thread_exit = options[:before_thread_exit]
56
59
  @reaping_time = options[:reaping_time]
@@ -79,30 +82,37 @@ module Puma
79
82
 
80
83
  attr_reader :spawned, :trim_requested, :waiting
81
84
 
82
- def self.clean_thread_locals
83
- Thread.current.keys.each do |key| # rubocop: disable Style/HashEachMethods
84
- Thread.current[key] = nil unless key == :__recursive_key__
85
- end
86
- end
87
-
88
85
  # generate stats hash so as not to perform multiple locks
89
86
  # @return [Hash] hash containing stat info from ThreadPool
90
87
  def stats
91
88
  with_mutex do
89
+ temp = @backlog_max
90
+ @backlog_max = 0
92
91
  { backlog: @todo.size,
93
92
  running: @spawned,
94
93
  pool_capacity: @waiting + (@max - @spawned),
95
- busy_threads: @spawned - @waiting + @todo.size
94
+ busy_threads: @spawned - @waiting + @todo.size,
95
+ backlog_max: temp
96
96
  }
97
97
  end
98
98
  end
99
99
 
100
+ def reset_max
101
+ with_mutex { @backlog_max = 0 }
102
+ end
103
+
100
104
  # How many objects have yet to be processed by the pool?
101
105
  #
102
106
  def backlog
103
107
  with_mutex { @todo.size }
104
108
  end
105
109
 
110
+ # The maximum size of the backlog
111
+ #
112
+ def backlog_max
113
+ with_mutex { @backlog_max }
114
+ end
115
+
106
116
  # @!attribute [r] pool_capacity
107
117
  def pool_capacity
108
118
  waiting + (@max - spawned)
@@ -159,10 +169,6 @@ module Puma
159
169
  work = todo.shift
160
170
  end
161
171
 
162
- if @clean_thread_locals
163
- ThreadPool.clean_thread_locals
164
- end
165
-
166
172
  begin
167
173
  @out_of_band_pending = true if block.call(work)
168
174
  rescue Exception => e
@@ -183,7 +189,7 @@ module Puma
183
189
 
184
190
  @before_thread_start.each do |b|
185
191
  begin
186
- b.call
192
+ b[:block].call
187
193
  rescue Exception => e
188
194
  STDERR.puts "WARNING before_thread_start hook failed with exception (#{e.class}) #{e.message}"
189
195
  end
@@ -198,7 +204,7 @@ module Puma
198
204
 
199
205
  @before_thread_exit.each do |b|
200
206
  begin
201
- b.call
207
+ b[:block].call
202
208
  rescue Exception => e
203
209
  STDERR.puts "WARNING before_thread_exit hook failed with exception (#{e.class}) #{e.message}"
204
210
  end
@@ -214,16 +220,27 @@ module Puma
214
220
 
215
221
  # we execute on idle hook when all threads are free
216
222
  return false unless @spawned == @waiting
217
-
218
- @out_of_band.each(&:call)
223
+ @out_of_band_running = true
224
+ @out_of_band.each { |b| b[:block].call }
219
225
  true
220
226
  rescue Exception => e
221
227
  STDERR.puts "Exception calling out_of_band_hook: #{e.message} (#{e.class})"
222
228
  true
229
+ ensure
230
+ @out_of_band_running = false
231
+ @out_of_band_condvar.broadcast
223
232
  end
224
233
 
225
234
  private :trigger_out_of_band_hook
226
235
 
236
+ def wait_while_out_of_band_running
237
+ return unless @out_of_band_running
238
+
239
+ with_mutex do
240
+ @out_of_band_condvar.wait(@mutex) while @out_of_band_running
241
+ end
242
+ end
243
+
227
244
  # @version 5.0.0
228
245
  def with_mutex(&block)
229
246
  @mutex.owned? ?
@@ -239,6 +256,8 @@ module Puma
239
256
  end
240
257
 
241
258
  @todo << work
259
+ t = @todo.size
260
+ @backlog_max = t if t > @backlog_max
242
261
 
243
262
  if @waiting < @todo.size and @spawned < @max
244
263
  spawn_thread
@@ -248,69 +267,6 @@ module Puma
248
267
  end
249
268
  end
250
269
 
251
- # This method is used by `Puma::Server` to let the server know when
252
- # the thread pool can pull more requests from the socket and
253
- # pass to the reactor.
254
- #
255
- # The general idea is that the thread pool can only work on a fixed
256
- # number of requests at the same time. If it is already processing that
257
- # number of requests then it is at capacity. If another Puma process has
258
- # spare capacity, then the request can be left on the socket so the other
259
- # worker can pick it up and process it.
260
- #
261
- # For example: if there are 5 threads, but only 4 working on
262
- # requests, this method will not wait and the `Puma::Server`
263
- # can pull a request right away.
264
- #
265
- # If there are 5 threads and all 5 of them are busy, then it will
266
- # pause here, and wait until the `not_full` condition variable is
267
- # signaled, usually this indicates that a request has been processed.
268
- #
269
- # It's important to note that even though the server might accept another
270
- # request, it might not be added to the `@todo` array right away.
271
- # For example if a slow client has only sent a header, but not a body
272
- # then the `@todo` array would stay the same size as the reactor works
273
- # to try to buffer the request. In that scenario the next call to this
274
- # method would not block and another request would be added into the reactor
275
- # by the server. This would continue until a fully buffered request
276
- # makes it through the reactor and can then be processed by the thread pool.
277
- def wait_until_not_full
278
- with_mutex do
279
- while true
280
- return if @shutdown
281
-
282
- # If we can still spin up new threads and there
283
- # is work queued that cannot be handled by waiting
284
- # threads, then accept more work until we would
285
- # spin up the max number of threads.
286
- return if busy_threads < @max
287
-
288
- @not_full.wait @mutex
289
- end
290
- end
291
- end
292
-
293
- # @version 5.0.0
294
- def wait_for_less_busy_worker(delay_s)
295
- return unless delay_s && delay_s > 0
296
-
297
- # Ruby MRI does GVL, this can result
298
- # in processing contention when multiple threads
299
- # (requests) are running concurrently
300
- return unless Puma.mri?
301
-
302
- with_mutex do
303
- return if @shutdown
304
-
305
- # do not delay, if we are not busy
306
- return unless busy_threads > 0
307
-
308
- # this will be signaled once a request finishes,
309
- # which can happen earlier than delay
310
- @not_full.wait @mutex, delay_s
311
- end
312
- end
313
-
314
270
  # If there are any free threads in the pool, tell one to go ahead
315
271
  # and exit. If +force+ is true, then a trim request is requested
316
272
  # even if all threads are being utilized.
data/lib/puma/util.rb CHANGED
@@ -10,13 +10,6 @@ module Puma
10
10
  IO.pipe
11
11
  end
12
12
 
13
- # An instance method on Thread has been provided to address https://bugs.ruby-lang.org/issues/13632,
14
- # which currently affects some older versions of Ruby: 2.2.7 2.2.8 2.2.9 2.2.10 2.3.4 2.4.1
15
- # Additional context: https://github.com/puma/puma/pull/1345
16
- def purge_interrupt_queue
17
- Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
18
- end
19
-
20
13
  # Escapes and unescapes a URI escaped string with
21
14
  # +encoding+. +encoding+ will be the target encoding of the string
22
15
  # returned, and it defaults to UTF-8
data/lib/puma.rb CHANGED
@@ -75,4 +75,14 @@ module Puma
75
75
  def self.set_thread_name(name)
76
76
  Thread.current.name = "puma #{name}"
77
77
  end
78
+
79
+ # Shows deprecated warning for renamed methods.
80
+ # @example
81
+ # Puma.deprecate_method_change :on_booted, __callee__, __method__
82
+ #
83
+ def self.deprecate_method_change(method_old, method_caller, method_new)
84
+ if method_old == method_caller
85
+ warn "Use '#{method_new}', '#{method_caller}' is deprecated and will be removed in v8"
86
+ end
87
+ end
78
88
  end
@@ -32,7 +32,7 @@ module Puma
32
32
 
33
33
  @events = options[:events] || ::Puma::Events.new
34
34
 
35
- conf = ::Puma::Configuration.new(options, default_options.merge({events: @events})) do |user_config, file_config, default_config|
35
+ conf = ::Puma::Configuration.new(options, default_options.merge({ events: @events })) do |user_config, file_config, default_config|
36
36
  if options.delete(:Verbose)
37
37
  begin
38
38
  require 'rack/commonlogger' # Rack 1.x
@@ -72,7 +72,7 @@ module Puma
72
72
 
73
73
  log_writer = options.delete(:Silent) ? ::Puma::LogWriter.strings : ::Puma::LogWriter.stdio
74
74
 
75
- launcher = ::Puma::Launcher.new(conf, :log_writer => log_writer, events: @events)
75
+ launcher = ::Puma::Launcher.new(conf, log_writer: log_writer, events: @events)
76
76
 
77
77
  yield launcher if block_given?
78
78
  begin
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: puma
3
3
  version: !ruby/object:Gem::Version
4
- version: 6.6.1
4
+ version: 7.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Evan Phoenix
@@ -138,7 +138,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
138
138
  requirements:
139
139
  - - ">="
140
140
  - !ruby/object:Gem::Version
141
- version: '2.4'
141
+ version: '3.0'
142
142
  required_rubygems_version: !ruby/object:Gem::Requirement
143
143
  requirements:
144
144
  - - ">="