puma 6.6.0 → 8.0.2
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.
- checksums.yaml +4 -4
- data/History.md +309 -5
- data/README.md +41 -42
- data/docs/5.0-Upgrade.md +98 -0
- data/docs/6.0-Upgrade.md +56 -0
- data/docs/7.0-Upgrade.md +52 -0
- data/docs/8.0-Upgrade.md +100 -0
- data/docs/deployment.md +58 -23
- data/docs/fork_worker.md +5 -5
- data/docs/grpc.md +62 -0
- data/docs/images/favicon.svg +1 -0
- data/docs/images/running-puma.svg +1 -0
- data/docs/images/standard-logo.svg +1 -0
- data/docs/jungle/README.md +1 -1
- data/docs/kubernetes.md +11 -16
- data/docs/plugins.md +2 -2
- data/docs/restart.md +2 -2
- data/docs/signals.md +21 -21
- data/docs/stats.md +4 -3
- data/docs/systemd.md +4 -4
- data/ext/puma_http11/extconf.rb +2 -17
- data/ext/puma_http11/http11_parser.java.rl +51 -65
- data/ext/puma_http11/mini_ssl.c +18 -8
- data/ext/puma_http11/org/jruby/puma/EnvKey.java +241 -0
- data/ext/puma_http11/org/jruby/puma/Http11.java +174 -102
- data/ext/puma_http11/org/jruby/puma/Http11Parser.java +71 -85
- data/ext/puma_http11/puma_http11.c +122 -118
- data/lib/puma/app/status.rb +10 -2
- data/lib/puma/binder.rb +12 -10
- data/lib/puma/cli.rb +4 -6
- data/lib/puma/client.rb +205 -131
- data/lib/puma/client_env.rb +171 -0
- data/lib/puma/cluster/worker.rb +17 -17
- data/lib/puma/cluster/worker_handle.rb +38 -7
- data/lib/puma/cluster.rb +44 -30
- data/lib/puma/cluster_accept_loop_delay.rb +91 -0
- data/lib/puma/commonlogger.rb +3 -3
- data/lib/puma/configuration.rb +173 -58
- data/lib/puma/const.rb +11 -11
- data/lib/puma/control_cli.rb +7 -3
- data/lib/puma/detect.rb +13 -0
- data/lib/puma/dsl.rb +225 -108
- data/lib/puma/error_logger.rb +3 -1
- data/lib/puma/events.rb +25 -10
- data/lib/puma/io_buffer.rb +8 -4
- data/lib/puma/launcher/bundle_pruner.rb +3 -5
- data/lib/puma/launcher.rb +57 -53
- data/lib/puma/log_writer.rb +8 -2
- data/lib/puma/minissl.rb +0 -1
- data/lib/puma/plugin/systemd.rb +3 -3
- data/lib/puma/rack/urlmap.rb +1 -1
- data/lib/puma/reactor.rb +19 -13
- data/lib/puma/{request.rb → response.rb} +56 -212
- data/lib/puma/runner.rb +9 -18
- data/lib/puma/server.rb +182 -97
- data/lib/puma/server_plugin_control.rb +32 -0
- data/lib/puma/single.rb +7 -4
- data/lib/puma/state_file.rb +3 -2
- data/lib/puma/thread_pool.rb +170 -99
- data/lib/puma/util.rb +0 -7
- data/lib/puma.rb +10 -0
- data/lib/rack/handler/puma.rb +3 -3
- data/tools/Dockerfile +15 -5
- metadata +19 -7
- data/ext/puma_http11/ext_help.h +0 -15
data/lib/puma/thread_pool.rb
CHANGED
|
@@ -3,8 +3,13 @@
|
|
|
3
3
|
require 'thread'
|
|
4
4
|
|
|
5
5
|
require_relative 'io_buffer'
|
|
6
|
+
require_relative 'server_plugin_control'
|
|
6
7
|
|
|
7
8
|
module Puma
|
|
9
|
+
|
|
10
|
+
# Add `Thread#puma_server` and `Thread#puma_server=`
|
|
11
|
+
Thread.attr_accessor(:puma_server)
|
|
12
|
+
|
|
8
13
|
# Internal Docs for A simple thread pool management object.
|
|
9
14
|
#
|
|
10
15
|
# Each Puma "worker" has a thread pool to process requests.
|
|
@@ -20,37 +25,90 @@ module Puma
|
|
|
20
25
|
class ForceShutdown < RuntimeError
|
|
21
26
|
end
|
|
22
27
|
|
|
28
|
+
class ProcessorThread
|
|
29
|
+
attr_accessor :thread
|
|
30
|
+
attr_writer :marked_as_io_thread
|
|
31
|
+
|
|
32
|
+
def initialize(pool)
|
|
33
|
+
@pool = pool
|
|
34
|
+
@thread = nil
|
|
35
|
+
@marked_as_io_thread = false
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def mark_as_io_thread!
|
|
39
|
+
unless @marked_as_io_thread
|
|
40
|
+
@marked_as_io_thread = true
|
|
41
|
+
|
|
42
|
+
# Immediately signal the pool that it can spawn a new thread
|
|
43
|
+
# if there's some work in the queue.
|
|
44
|
+
@pool.spawn_thread_if_needed
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def marked_as_io_thread?
|
|
49
|
+
@marked_as_io_thread
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def alive?
|
|
53
|
+
@thread&.alive?
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def join(...)
|
|
57
|
+
@thread.join(...)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def kill(...)
|
|
61
|
+
@thread.kill(...)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def [](key)
|
|
65
|
+
@thread[key]
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def raise(...)
|
|
69
|
+
@thread.raise(...)
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
23
73
|
# How long, after raising the ForceShutdown of a thread during
|
|
24
74
|
# forced shutdown mode, to wait for the thread to try and finish
|
|
25
75
|
# up its work before leaving the thread to die on the vine.
|
|
26
76
|
SHUTDOWN_GRACE_TIME = 5 # seconds
|
|
27
77
|
|
|
78
|
+
attr_reader :out_of_band_running
|
|
79
|
+
|
|
28
80
|
# Maintain a minimum of +min+ and maximum of +max+ threads
|
|
29
81
|
# in the pool.
|
|
30
82
|
#
|
|
31
83
|
# The block passed is the work that will be performed in each
|
|
32
84
|
# thread.
|
|
33
85
|
#
|
|
34
|
-
def initialize(name, options = {}, &block)
|
|
86
|
+
def initialize(name, options = {}, server: nil, &block)
|
|
87
|
+
@server = server
|
|
88
|
+
|
|
35
89
|
@not_empty = ConditionVariable.new
|
|
36
90
|
@not_full = ConditionVariable.new
|
|
37
91
|
@mutex = Mutex.new
|
|
92
|
+
@todo = Queue.new
|
|
38
93
|
|
|
39
|
-
@
|
|
40
|
-
|
|
94
|
+
@backlog_max = 0
|
|
41
95
|
@spawned = 0
|
|
42
96
|
@waiting = 0
|
|
43
97
|
|
|
44
98
|
@name = name
|
|
45
99
|
@min = Integer(options[:min_threads])
|
|
46
100
|
@max = Integer(options[:max_threads])
|
|
101
|
+
@max_io_threads = Integer(options[:max_io_threads] || 0)
|
|
102
|
+
|
|
47
103
|
# Not an 'exposed' option, options[:pool_shutdown_grace_time] is used in CI
|
|
48
104
|
# to shorten @shutdown_grace_time from SHUTDOWN_GRACE_TIME. Parallel CI
|
|
49
105
|
# makes stubbing constants difficult.
|
|
50
106
|
@shutdown_grace_time = Float(options[:pool_shutdown_grace_time] || SHUTDOWN_GRACE_TIME)
|
|
107
|
+
@shutdown_debug = options[:shutdown_debug]
|
|
51
108
|
@block = block
|
|
52
109
|
@out_of_band = options[:out_of_band]
|
|
53
|
-
@
|
|
110
|
+
@out_of_band_running = false
|
|
111
|
+
@out_of_band_condvar = ConditionVariable.new
|
|
54
112
|
@before_thread_start = options[:before_thread_start]
|
|
55
113
|
@before_thread_exit = options[:before_thread_exit]
|
|
56
114
|
@reaping_time = options[:reaping_time]
|
|
@@ -61,7 +119,7 @@ module Puma
|
|
|
61
119
|
@trim_requested = 0
|
|
62
120
|
@out_of_band_pending = false
|
|
63
121
|
|
|
64
|
-
@
|
|
122
|
+
@processors = []
|
|
65
123
|
|
|
66
124
|
@auto_trim = nil
|
|
67
125
|
@reaper = nil
|
|
@@ -78,34 +136,43 @@ module Puma
|
|
|
78
136
|
end
|
|
79
137
|
|
|
80
138
|
attr_reader :spawned, :trim_requested, :waiting
|
|
81
|
-
|
|
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
|
|
139
|
+
attr_accessor :min, :max
|
|
87
140
|
|
|
88
141
|
# generate stats hash so as not to perform multiple locks
|
|
89
142
|
# @return [Hash] hash containing stat info from ThreadPool
|
|
90
143
|
def stats
|
|
91
144
|
with_mutex do
|
|
145
|
+
temp = @backlog_max
|
|
146
|
+
@backlog_max = 0
|
|
92
147
|
{ backlog: @todo.size,
|
|
93
148
|
running: @spawned,
|
|
94
|
-
pool_capacity:
|
|
95
|
-
busy_threads: @spawned - @waiting + @todo.size
|
|
149
|
+
pool_capacity: pool_capacity,
|
|
150
|
+
busy_threads: @spawned - @waiting + @todo.size,
|
|
151
|
+
io_threads: @processors.count(&:marked_as_io_thread?),
|
|
152
|
+
backlog_max: temp
|
|
96
153
|
}
|
|
97
154
|
end
|
|
98
155
|
end
|
|
99
156
|
|
|
157
|
+
def reset_max
|
|
158
|
+
with_mutex { @backlog_max = 0 }
|
|
159
|
+
end
|
|
160
|
+
|
|
100
161
|
# How many objects have yet to be processed by the pool?
|
|
101
162
|
#
|
|
102
163
|
def backlog
|
|
103
164
|
with_mutex { @todo.size }
|
|
104
165
|
end
|
|
105
166
|
|
|
167
|
+
# The maximum size of the backlog
|
|
168
|
+
#
|
|
169
|
+
def backlog_max
|
|
170
|
+
with_mutex { @backlog_max }
|
|
171
|
+
end
|
|
172
|
+
|
|
106
173
|
# @!attribute [r] pool_capacity
|
|
107
174
|
def pool_capacity
|
|
108
|
-
waiting + (@max - spawned)
|
|
175
|
+
(waiting + (@max - spawned)).clamp(0, Float::INFINITY)
|
|
109
176
|
end
|
|
110
177
|
|
|
111
178
|
# @!attribute [r] busy_threads
|
|
@@ -122,8 +189,12 @@ module Puma
|
|
|
122
189
|
@spawned += 1
|
|
123
190
|
|
|
124
191
|
trigger_before_thread_start_hooks
|
|
125
|
-
|
|
192
|
+
processor = ProcessorThread.new(self)
|
|
193
|
+
processor.thread = Thread.new(processor, @spawned) do |processor, spawned|
|
|
126
194
|
Puma.set_thread_name '%s tp %03i' % [@name, spawned]
|
|
195
|
+
# Advertise server into the thread
|
|
196
|
+
Thread.current.puma_server = @server
|
|
197
|
+
|
|
127
198
|
todo = @todo
|
|
128
199
|
block = @block
|
|
129
200
|
mutex = @mutex
|
|
@@ -134,11 +205,23 @@ module Puma
|
|
|
134
205
|
work = nil
|
|
135
206
|
|
|
136
207
|
mutex.synchronize do
|
|
208
|
+
if processor.marked_as_io_thread?
|
|
209
|
+
if @processors.count { |t| !t.marked_as_io_thread? } < @max
|
|
210
|
+
# We're not at max processor threads, so the io thread can rejoin the normal population.
|
|
211
|
+
processor.marked_as_io_thread = false
|
|
212
|
+
else
|
|
213
|
+
# We're already at max threads, so we exit the extra io thread.
|
|
214
|
+
@processors.delete(processor)
|
|
215
|
+
trigger_before_thread_exit_hooks
|
|
216
|
+
Thread.exit
|
|
217
|
+
end
|
|
218
|
+
end
|
|
219
|
+
|
|
137
220
|
while todo.empty?
|
|
138
221
|
if @trim_requested > 0
|
|
139
222
|
@trim_requested -= 1
|
|
140
223
|
@spawned -= 1
|
|
141
|
-
@
|
|
224
|
+
@processors.delete(processor)
|
|
142
225
|
not_full.signal
|
|
143
226
|
trigger_before_thread_exit_hooks
|
|
144
227
|
Thread.exit
|
|
@@ -159,21 +242,17 @@ module Puma
|
|
|
159
242
|
work = todo.shift
|
|
160
243
|
end
|
|
161
244
|
|
|
162
|
-
if @clean_thread_locals
|
|
163
|
-
ThreadPool.clean_thread_locals
|
|
164
|
-
end
|
|
165
|
-
|
|
166
245
|
begin
|
|
167
|
-
@out_of_band_pending = true if block.call(work)
|
|
246
|
+
@out_of_band_pending = true if block.call(processor, work)
|
|
168
247
|
rescue Exception => e
|
|
169
248
|
STDERR.puts "Error reached top of thread-pool: #{e.message} (#{e.class})"
|
|
170
249
|
end
|
|
171
250
|
end
|
|
172
251
|
end
|
|
173
252
|
|
|
174
|
-
@
|
|
253
|
+
@processors << processor
|
|
175
254
|
|
|
176
|
-
|
|
255
|
+
processor
|
|
177
256
|
end
|
|
178
257
|
|
|
179
258
|
private :spawn_thread
|
|
@@ -183,7 +262,7 @@ module Puma
|
|
|
183
262
|
|
|
184
263
|
@before_thread_start.each do |b|
|
|
185
264
|
begin
|
|
186
|
-
b.call
|
|
265
|
+
b[:block].call(ServerPluginControl.new(@server))
|
|
187
266
|
rescue Exception => e
|
|
188
267
|
STDERR.puts "WARNING before_thread_start hook failed with exception (#{e.class}) #{e.message}"
|
|
189
268
|
end
|
|
@@ -198,7 +277,7 @@ module Puma
|
|
|
198
277
|
|
|
199
278
|
@before_thread_exit.each do |b|
|
|
200
279
|
begin
|
|
201
|
-
b.call
|
|
280
|
+
b[:block].call
|
|
202
281
|
rescue Exception => e
|
|
203
282
|
STDERR.puts "WARNING before_thread_exit hook failed with exception (#{e.class}) #{e.message}"
|
|
204
283
|
end
|
|
@@ -214,16 +293,27 @@ module Puma
|
|
|
214
293
|
|
|
215
294
|
# we execute on idle hook when all threads are free
|
|
216
295
|
return false unless @spawned == @waiting
|
|
217
|
-
|
|
218
|
-
@out_of_band.each
|
|
296
|
+
@out_of_band_running = true
|
|
297
|
+
@out_of_band.each { |b| b[:block].call }
|
|
219
298
|
true
|
|
220
299
|
rescue Exception => e
|
|
221
300
|
STDERR.puts "Exception calling out_of_band_hook: #{e.message} (#{e.class})"
|
|
222
301
|
true
|
|
302
|
+
ensure
|
|
303
|
+
@out_of_band_running = false
|
|
304
|
+
@out_of_band_condvar.broadcast
|
|
223
305
|
end
|
|
224
306
|
|
|
225
307
|
private :trigger_out_of_band_hook
|
|
226
308
|
|
|
309
|
+
def wait_while_out_of_band_running
|
|
310
|
+
return unless @out_of_band_running
|
|
311
|
+
|
|
312
|
+
with_mutex do
|
|
313
|
+
@out_of_band_condvar.wait(@mutex) while @out_of_band_running
|
|
314
|
+
end
|
|
315
|
+
end
|
|
316
|
+
|
|
227
317
|
# @version 5.0.0
|
|
228
318
|
def with_mutex(&block)
|
|
229
319
|
@mutex.owned? ?
|
|
@@ -231,6 +321,16 @@ module Puma
|
|
|
231
321
|
@mutex.synchronize(&block)
|
|
232
322
|
end
|
|
233
323
|
|
|
324
|
+
# :nodoc:
|
|
325
|
+
#
|
|
326
|
+
# Must be called with @mutex held!
|
|
327
|
+
#
|
|
328
|
+
def can_spawn_processor?
|
|
329
|
+
io_processors_count = @processors.count(&:marked_as_io_thread?)
|
|
330
|
+
extra_io_processors_count = io_processors_count > @max_io_threads ? io_processors_count - @max_io_threads : 0
|
|
331
|
+
(@spawned - io_processors_count) < (@max - extra_io_processors_count)
|
|
332
|
+
end
|
|
333
|
+
|
|
234
334
|
# Add +work+ to the todo list for a Thread to pickup and process.
|
|
235
335
|
def <<(work)
|
|
236
336
|
with_mutex do
|
|
@@ -239,78 +339,26 @@ module Puma
|
|
|
239
339
|
end
|
|
240
340
|
|
|
241
341
|
@todo << work
|
|
342
|
+
t = @todo.size
|
|
343
|
+
@backlog_max = t if t > @backlog_max
|
|
242
344
|
|
|
243
|
-
if @waiting < @todo.size and
|
|
345
|
+
if @waiting < @todo.size and can_spawn_processor?
|
|
244
346
|
spawn_thread
|
|
245
347
|
end
|
|
246
348
|
|
|
247
349
|
@not_empty.signal
|
|
248
350
|
end
|
|
351
|
+
self
|
|
249
352
|
end
|
|
250
353
|
|
|
251
|
-
|
|
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
|
|
354
|
+
def spawn_thread_if_needed # :nodoc:
|
|
278
355
|
with_mutex do
|
|
279
|
-
|
|
280
|
-
|
|
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
|
|
356
|
+
if @waiting < @todo.size and can_spawn_processor?
|
|
357
|
+
spawn_thread
|
|
289
358
|
end
|
|
290
359
|
end
|
|
291
360
|
end
|
|
292
361
|
|
|
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
362
|
# If there are any free threads in the pool, tell one to go ahead
|
|
315
363
|
# and exit. If +force+ is true, then a trim request is requested
|
|
316
364
|
# even if all threads are being utilized.
|
|
@@ -329,16 +377,12 @@ module Puma
|
|
|
329
377
|
# spawned counter so that new healthy threads could be created again.
|
|
330
378
|
def reap
|
|
331
379
|
with_mutex do
|
|
332
|
-
|
|
380
|
+
@processors, dead_processors = @processors.partition(&:alive?)
|
|
333
381
|
|
|
334
|
-
|
|
335
|
-
|
|
382
|
+
dead_processors.each do |processor|
|
|
383
|
+
processor.kill
|
|
336
384
|
@spawned -= 1
|
|
337
385
|
end
|
|
338
|
-
|
|
339
|
-
@workers.delete_if do |w|
|
|
340
|
-
dead_workers.include?(w)
|
|
341
|
-
end
|
|
342
386
|
end
|
|
343
387
|
end
|
|
344
388
|
|
|
@@ -397,7 +441,7 @@ module Puma
|
|
|
397
441
|
# Next, wait an extra +@shutdown_grace_time+ seconds then force-kill remaining
|
|
398
442
|
# threads. Finally, wait 1 second for remaining threads to exit.
|
|
399
443
|
#
|
|
400
|
-
def shutdown(timeout
|
|
444
|
+
def shutdown(timeout)
|
|
401
445
|
threads = with_mutex do
|
|
402
446
|
@shutdown = true
|
|
403
447
|
@trim_requested = @spawned
|
|
@@ -406,8 +450,12 @@ module Puma
|
|
|
406
450
|
|
|
407
451
|
@auto_trim&.stop
|
|
408
452
|
@reaper&.stop
|
|
409
|
-
# dup
|
|
410
|
-
@
|
|
453
|
+
# dup processors so that we join them all safely
|
|
454
|
+
@processors.dup
|
|
455
|
+
end
|
|
456
|
+
|
|
457
|
+
if @shutdown_debug == true
|
|
458
|
+
shutdown_debug("Shutdown initiated")
|
|
411
459
|
end
|
|
412
460
|
|
|
413
461
|
if timeout == -1
|
|
@@ -417,13 +465,16 @@ module Puma
|
|
|
417
465
|
join = ->(inner_timeout) do
|
|
418
466
|
start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
419
467
|
threads.reject! do |t|
|
|
420
|
-
|
|
421
|
-
t.join
|
|
468
|
+
remaining = inner_timeout - (Process.clock_gettime(Process::CLOCK_MONOTONIC) - start)
|
|
469
|
+
remaining > 0 && t.join(remaining)
|
|
422
470
|
end
|
|
423
471
|
end
|
|
424
472
|
|
|
425
473
|
# Wait +timeout+ seconds for threads to finish.
|
|
426
474
|
join.call(timeout)
|
|
475
|
+
if @shutdown_debug == :on_force && !threads.empty?
|
|
476
|
+
shutdown_debug("Shutdown timeout exceeded")
|
|
477
|
+
end
|
|
427
478
|
|
|
428
479
|
# If threads are still running, raise ForceShutdown and wait to finish.
|
|
429
480
|
@shutdown_mutex.synchronize do
|
|
@@ -433,6 +484,9 @@ module Puma
|
|
|
433
484
|
end
|
|
434
485
|
end
|
|
435
486
|
join.call(@shutdown_grace_time)
|
|
487
|
+
if @shutdown_debug == :on_force && !threads.empty?
|
|
488
|
+
shutdown_debug("Shutdown grace timeout exceeded")
|
|
489
|
+
end
|
|
436
490
|
|
|
437
491
|
# If threads are _still_ running, forcefully kill them and wait to finish.
|
|
438
492
|
threads.each(&:kill)
|
|
@@ -440,7 +494,24 @@ module Puma
|
|
|
440
494
|
end
|
|
441
495
|
|
|
442
496
|
@spawned = 0
|
|
443
|
-
@
|
|
497
|
+
@processors = []
|
|
498
|
+
end
|
|
499
|
+
|
|
500
|
+
private
|
|
501
|
+
|
|
502
|
+
def shutdown_debug(message)
|
|
503
|
+
pid = Process.pid
|
|
504
|
+
threads = Thread.list
|
|
505
|
+
|
|
506
|
+
$stdout.syswrite "#{pid}: #{message}\n"
|
|
507
|
+
$stdout.syswrite "#{pid}: === Begin thread backtrace dump ===\n"
|
|
508
|
+
|
|
509
|
+
threads.each_with_index do |thread, index|
|
|
510
|
+
$stdout.syswrite "#{pid}: Thread #{index + 1}/#{threads.size}: #{thread.inspect}\n"
|
|
511
|
+
$stdout.syswrite "#{pid}: #{(thread.backtrace || []).join("\n#{pid}: ")}\n\n"
|
|
512
|
+
end
|
|
513
|
+
|
|
514
|
+
$stdout.syswrite "#{pid}: === End thread backtrace dump ===\n"
|
|
444
515
|
end
|
|
445
516
|
end
|
|
446
517
|
end
|
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
|
data/lib/rack/handler/puma.rb
CHANGED
|
@@ -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, :
|
|
75
|
+
launcher = ::Puma::Launcher.new(conf, log_writer: log_writer, events: @events)
|
|
76
76
|
|
|
77
77
|
yield launcher if block_given?
|
|
78
78
|
begin
|
|
@@ -109,7 +109,7 @@ module Puma
|
|
|
109
109
|
end
|
|
110
110
|
|
|
111
111
|
if port
|
|
112
|
-
host ||= ::Puma::Configuration
|
|
112
|
+
host ||= ::Puma::Configuration.default_tcp_host
|
|
113
113
|
config.port port, host
|
|
114
114
|
end
|
|
115
115
|
end
|
data/tools/Dockerfile
CHANGED
|
@@ -1,16 +1,26 @@
|
|
|
1
1
|
# Use this Dockerfile to create minimal reproductions of issues
|
|
2
|
+
# Build (MRI): docker build -f tools/Dockerfile .
|
|
3
|
+
# Build (JRuby): docker build -f tools/Dockerfile --build-arg RUBY_IMAGE=jruby:9.4 .
|
|
2
4
|
|
|
3
|
-
|
|
5
|
+
ARG RUBY_IMAGE=ruby:latest
|
|
6
|
+
FROM ${RUBY_IMAGE}
|
|
4
7
|
|
|
5
|
-
#
|
|
6
|
-
|
|
8
|
+
# Set BUNDLE_FROZEN=false if you need to update Gemfile.lock during a build.
|
|
9
|
+
ARG BUNDLE_FROZEN=true
|
|
10
|
+
|
|
11
|
+
RUN apt-get update \
|
|
12
|
+
&& apt-get install -y --no-install-recommends ragel procps git \
|
|
13
|
+
&& rm -rf /var/lib/apt/lists/*
|
|
14
|
+
|
|
15
|
+
# Only freeze Bundler and compile native extensions when using MRI.
|
|
16
|
+
RUN if [ "$(ruby -e 'print RUBY_ENGINE')" = "ruby" ] && [ "${BUNDLE_FROZEN}" = "true" ]; then bundle config --global frozen 1; fi
|
|
7
17
|
|
|
8
18
|
WORKDIR /usr/src/app
|
|
9
19
|
|
|
10
20
|
COPY . .
|
|
11
21
|
|
|
12
22
|
RUN bundle install
|
|
13
|
-
RUN bundle exec rake compile
|
|
23
|
+
RUN if [ "$(ruby -e 'print RUBY_ENGINE')" = "ruby" ]; then bundle exec rake compile; fi
|
|
14
24
|
|
|
15
25
|
EXPOSE 9292
|
|
16
|
-
CMD bundle exec bin/puma test/rackup/hello.ru
|
|
26
|
+
CMD ["bundle", "exec", "bin/puma", "test/rackup/hello.ru"]
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: puma
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 8.0.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Evan Phoenix
|
|
8
8
|
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: nio4r
|
|
@@ -43,13 +43,21 @@ files:
|
|
|
43
43
|
- bin/puma
|
|
44
44
|
- bin/puma-wild
|
|
45
45
|
- bin/pumactl
|
|
46
|
+
- docs/5.0-Upgrade.md
|
|
47
|
+
- docs/6.0-Upgrade.md
|
|
48
|
+
- docs/7.0-Upgrade.md
|
|
49
|
+
- docs/8.0-Upgrade.md
|
|
46
50
|
- docs/architecture.md
|
|
47
51
|
- docs/compile_options.md
|
|
48
52
|
- docs/deployment.md
|
|
49
53
|
- docs/fork_worker.md
|
|
54
|
+
- docs/grpc.md
|
|
55
|
+
- docs/images/favicon.svg
|
|
50
56
|
- docs/images/puma-connection-flow-no-reactor.png
|
|
51
57
|
- docs/images/puma-connection-flow.png
|
|
52
58
|
- docs/images/puma-general-arch.png
|
|
59
|
+
- docs/images/running-puma.svg
|
|
60
|
+
- docs/images/standard-logo.svg
|
|
53
61
|
- docs/java_options.md
|
|
54
62
|
- docs/jungle/README.md
|
|
55
63
|
- docs/jungle/rc.d/README.md
|
|
@@ -66,7 +74,6 @@ files:
|
|
|
66
74
|
- docs/testing_benchmarks_local_files.md
|
|
67
75
|
- docs/testing_test_rackup_ci_files.md
|
|
68
76
|
- ext/puma_http11/PumaHttp11Service.java
|
|
69
|
-
- ext/puma_http11/ext_help.h
|
|
70
77
|
- ext/puma_http11/extconf.rb
|
|
71
78
|
- ext/puma_http11/http11_parser.c
|
|
72
79
|
- ext/puma_http11/http11_parser.h
|
|
@@ -75,6 +82,7 @@ files:
|
|
|
75
82
|
- ext/puma_http11/http11_parser_common.rl
|
|
76
83
|
- ext/puma_http11/mini_ssl.c
|
|
77
84
|
- ext/puma_http11/no_ssl/PumaHttp11Service.java
|
|
85
|
+
- ext/puma_http11/org/jruby/puma/EnvKey.java
|
|
78
86
|
- ext/puma_http11/org/jruby/puma/Http11.java
|
|
79
87
|
- ext/puma_http11/org/jruby/puma/Http11Parser.java
|
|
80
88
|
- ext/puma_http11/org/jruby/puma/MiniSSL.java
|
|
@@ -84,9 +92,11 @@ files:
|
|
|
84
92
|
- lib/puma/binder.rb
|
|
85
93
|
- lib/puma/cli.rb
|
|
86
94
|
- lib/puma/client.rb
|
|
95
|
+
- lib/puma/client_env.rb
|
|
87
96
|
- lib/puma/cluster.rb
|
|
88
97
|
- lib/puma/cluster/worker.rb
|
|
89
98
|
- lib/puma/cluster/worker_handle.rb
|
|
99
|
+
- lib/puma/cluster_accept_loop_delay.rb
|
|
90
100
|
- lib/puma/commonlogger.rb
|
|
91
101
|
- lib/puma/configuration.rb
|
|
92
102
|
- lib/puma/const.rb
|
|
@@ -111,10 +121,11 @@ files:
|
|
|
111
121
|
- lib/puma/rack/urlmap.rb
|
|
112
122
|
- lib/puma/rack_default.rb
|
|
113
123
|
- lib/puma/reactor.rb
|
|
114
|
-
- lib/puma/
|
|
124
|
+
- lib/puma/response.rb
|
|
115
125
|
- lib/puma/runner.rb
|
|
116
126
|
- lib/puma/sd_notify.rb
|
|
117
127
|
- lib/puma/server.rb
|
|
128
|
+
- lib/puma/server_plugin_control.rb
|
|
118
129
|
- lib/puma/single.rb
|
|
119
130
|
- lib/puma/state_file.rb
|
|
120
131
|
- lib/puma/thread_pool.rb
|
|
@@ -127,10 +138,11 @@ licenses:
|
|
|
127
138
|
- BSD-3-Clause
|
|
128
139
|
metadata:
|
|
129
140
|
bug_tracker_uri: https://github.com/puma/puma/issues
|
|
130
|
-
changelog_uri: https://github.com/puma/puma/blob/
|
|
141
|
+
changelog_uri: https://github.com/puma/puma/blob/main/History.md
|
|
131
142
|
homepage_uri: https://puma.io
|
|
132
143
|
source_code_uri: https://github.com/puma/puma
|
|
133
144
|
rubygems_mfa_required: 'true'
|
|
145
|
+
msys2_mingw_dependencies: openssl
|
|
134
146
|
rdoc_options: []
|
|
135
147
|
require_paths:
|
|
136
148
|
- lib
|
|
@@ -138,14 +150,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
138
150
|
requirements:
|
|
139
151
|
- - ">="
|
|
140
152
|
- !ruby/object:Gem::Version
|
|
141
|
-
version: '
|
|
153
|
+
version: '3.0'
|
|
142
154
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
143
155
|
requirements:
|
|
144
156
|
- - ">="
|
|
145
157
|
- !ruby/object:Gem::Version
|
|
146
158
|
version: '0'
|
|
147
159
|
requirements: []
|
|
148
|
-
rubygems_version:
|
|
160
|
+
rubygems_version: 4.0.6
|
|
149
161
|
specification_version: 4
|
|
150
162
|
summary: A Ruby/Rack web server built for parallelism.
|
|
151
163
|
test_files: []
|
data/ext/puma_http11/ext_help.h
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
#ifndef ext_help_h
|
|
2
|
-
#define ext_help_h
|
|
3
|
-
|
|
4
|
-
#define RAISE_NOT_NULL(T) if(T == NULL) rb_raise(rb_eArgError, "%s", "NULL found for " # T " when shouldn't be.");
|
|
5
|
-
#define DATA_GET(from,type,data_type,name) TypedData_Get_Struct(from,type,data_type,name); RAISE_NOT_NULL(name);
|
|
6
|
-
#define REQUIRE_TYPE(V, T) if(TYPE(V) != T) rb_raise(rb_eTypeError, "%s", "Wrong argument type for " # V " required " # T);
|
|
7
|
-
#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
|
|
8
|
-
|
|
9
|
-
#ifdef DEBUG
|
|
10
|
-
#define TRACE() fprintf(stderr, "> %s:%d:%s\n", __FILE__, __LINE__, __FUNCTION__)
|
|
11
|
-
#else
|
|
12
|
-
#define TRACE()
|
|
13
|
-
#endif
|
|
14
|
-
|
|
15
|
-
#endif
|