puma 7.2.0-java → 8.0.1-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.
- checksums.yaml +4 -4
- data/History.md +61 -0
- data/README.md +1 -2
- 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/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/signals.md +1 -1
- data/ext/puma_http11/http11_parser.java.rl +51 -65
- data/ext/puma_http11/org/jruby/puma/EnvKey.java +241 -0
- data/ext/puma_http11/org/jruby/puma/Http11.java +168 -104
- data/ext/puma_http11/org/jruby/puma/Http11Parser.java +71 -85
- data/lib/puma/binder.rb +2 -2
- data/lib/puma/cli.rb +1 -1
- data/lib/puma/client.rb +90 -66
- data/lib/puma/client_env.rb +171 -0
- data/lib/puma/cluster.rb +1 -1
- data/lib/puma/configuration.rb +70 -8
- data/lib/puma/const.rb +2 -2
- data/lib/puma/control_cli.rb +1 -1
- data/lib/puma/detect.rb +11 -0
- data/lib/puma/dsl.rb +74 -8
- data/lib/puma/launcher/bundle_pruner.rb +2 -4
- data/lib/puma/launcher.rb +3 -4
- data/lib/puma/log_writer.rb +8 -2
- data/lib/puma/puma_http11.jar +0 -0
- data/lib/puma/{request.rb → response.rb} +15 -186
- data/lib/puma/server.rb +70 -35
- data/lib/puma/server_plugin_control.rb +32 -0
- data/lib/puma/thread_pool.rb +129 -23
- data/lib/rack/handler/puma.rb +1 -1
- metadata +14 -3
data/lib/puma/server.rb
CHANGED
|
@@ -11,7 +11,7 @@ require_relative 'reactor'
|
|
|
11
11
|
require_relative 'client'
|
|
12
12
|
require_relative 'binder'
|
|
13
13
|
require_relative 'util'
|
|
14
|
-
require_relative '
|
|
14
|
+
require_relative 'response'
|
|
15
15
|
require_relative 'configuration'
|
|
16
16
|
require_relative 'cluster_accept_loop_delay'
|
|
17
17
|
|
|
@@ -31,15 +31,15 @@ module Puma
|
|
|
31
31
|
# Each `Puma::Server` will have one reactor and one thread pool.
|
|
32
32
|
class Server
|
|
33
33
|
module FiberPerRequest
|
|
34
|
-
def handle_request(client, requests)
|
|
34
|
+
def handle_request(processor, client, requests)
|
|
35
35
|
Fiber.new do
|
|
36
36
|
super
|
|
37
37
|
end.resume
|
|
38
38
|
end
|
|
39
39
|
end
|
|
40
40
|
|
|
41
|
-
include
|
|
42
|
-
include
|
|
41
|
+
include Const
|
|
42
|
+
include Response
|
|
43
43
|
|
|
44
44
|
attr_reader :options
|
|
45
45
|
attr_reader :thread
|
|
@@ -259,7 +259,9 @@ module Puma
|
|
|
259
259
|
|
|
260
260
|
@status = :run
|
|
261
261
|
|
|
262
|
-
@thread_pool = ThreadPool.new(thread_name, options, server: self)
|
|
262
|
+
@thread_pool = ThreadPool.new(thread_name, options, server: self) do |processor, client|
|
|
263
|
+
process_client(processor, client)
|
|
264
|
+
end
|
|
263
265
|
|
|
264
266
|
if @queue_requests
|
|
265
267
|
@reactor = Reactor.new(@io_selector_backend) { |c|
|
|
@@ -304,7 +306,7 @@ module Puma
|
|
|
304
306
|
# The method checks to see if it has the full header and body with
|
|
305
307
|
# the `Puma::Client#try_to_finish` method. If the full request has been sent,
|
|
306
308
|
# then the request is passed to the ThreadPool (`@thread_pool << client`)
|
|
307
|
-
# so that a "
|
|
309
|
+
# so that a "processor thread" can pick up the request and begin to execute application logic.
|
|
308
310
|
# The Client is then removed from the reactor (return `true`).
|
|
309
311
|
#
|
|
310
312
|
# If a client object times out, a 408 response is written, its connection is closed,
|
|
@@ -401,6 +403,7 @@ module Puma
|
|
|
401
403
|
next
|
|
402
404
|
end
|
|
403
405
|
drain += 1 if shutting_down?
|
|
406
|
+
|
|
404
407
|
client = new_client(io, sock)
|
|
405
408
|
client.send(addr_send_name, addr_value) if addr_value
|
|
406
409
|
pool << client
|
|
@@ -414,7 +417,7 @@ module Puma
|
|
|
414
417
|
end
|
|
415
418
|
end
|
|
416
419
|
|
|
417
|
-
@log_writer.debug "Drained #{drain} additional connections." if drain
|
|
420
|
+
@log_writer.debug { "Drained #{drain} additional connections." } if drain
|
|
418
421
|
@events.fire :state, @status
|
|
419
422
|
|
|
420
423
|
if queue_requests
|
|
@@ -444,7 +447,9 @@ module Puma
|
|
|
444
447
|
def new_client(io, sock)
|
|
445
448
|
client = Client.new(io, @binder.env(sock))
|
|
446
449
|
client.listener = sock
|
|
450
|
+
client.env_set_http_version = @env_set_http_version
|
|
447
451
|
client.http_content_length_limit = @http_content_length_limit
|
|
452
|
+
client.supported_http_methods = @supported_http_methods
|
|
448
453
|
client
|
|
449
454
|
end
|
|
450
455
|
|
|
@@ -470,14 +475,14 @@ module Puma
|
|
|
470
475
|
# Given a connection on +client+, handle the incoming requests,
|
|
471
476
|
# or queue the connection in the Reactor if no request is available.
|
|
472
477
|
#
|
|
473
|
-
# This method is called from a ThreadPool
|
|
478
|
+
# This method is called from a ThreadPool processor thread.
|
|
474
479
|
#
|
|
475
480
|
# This method supports HTTP Keep-Alive so it may, depending on if the client
|
|
476
481
|
# indicates that it supports keep alive, wait for another request before
|
|
477
482
|
# returning.
|
|
478
483
|
#
|
|
479
484
|
# Return true if one or more requests were processed.
|
|
480
|
-
def process_client(client)
|
|
485
|
+
def process_client(processor, client)
|
|
481
486
|
close_socket = true
|
|
482
487
|
|
|
483
488
|
requests = 0
|
|
@@ -500,7 +505,7 @@ module Puma
|
|
|
500
505
|
while can_loop
|
|
501
506
|
can_loop = false
|
|
502
507
|
@requests_count += 1
|
|
503
|
-
case handle_request(client, requests + 1)
|
|
508
|
+
case handle_request(processor, client, requests + 1)
|
|
504
509
|
when :close
|
|
505
510
|
when :async
|
|
506
511
|
close_socket = false
|
|
@@ -577,7 +582,7 @@ module Puma
|
|
|
577
582
|
lowlevel_error(e, client.env)
|
|
578
583
|
@log_writer.ssl_error e, client.io
|
|
579
584
|
when HttpParserError
|
|
580
|
-
response_to_error(client, requests, e, 400)
|
|
585
|
+
response_to_error(client, requests, e, client.error_status_code || 400)
|
|
581
586
|
@log_writer.parse_error e, client
|
|
582
587
|
when HttpParserError501
|
|
583
588
|
response_to_error(client, requests, e, 501)
|
|
@@ -610,7 +615,15 @@ module Puma
|
|
|
610
615
|
end
|
|
611
616
|
|
|
612
617
|
def response_to_error(client, requests, err, status_code)
|
|
613
|
-
|
|
618
|
+
# @todo remove sometime later
|
|
619
|
+
if status_code == 413
|
|
620
|
+
status = 413
|
|
621
|
+
res_body = ["Payload Too Large"]
|
|
622
|
+
headers = {}
|
|
623
|
+
headers[CONTENT_LENGTH2] = 17
|
|
624
|
+
else
|
|
625
|
+
status, headers, res_body = lowlevel_error(err, client.env, status_code)
|
|
626
|
+
end
|
|
614
627
|
prepare_response(status, headers, res_body, requests, client)
|
|
615
628
|
end
|
|
616
629
|
private :response_to_error
|
|
@@ -618,32 +631,11 @@ module Puma
|
|
|
618
631
|
# Wait for all outstanding requests to finish.
|
|
619
632
|
#
|
|
620
633
|
def graceful_shutdown
|
|
621
|
-
if options[:shutdown_debug]
|
|
622
|
-
threads = Thread.list
|
|
623
|
-
total = threads.size
|
|
624
|
-
|
|
625
|
-
pid = Process.pid
|
|
626
|
-
|
|
627
|
-
$stdout.syswrite "#{pid}: === Begin thread backtrace dump ===\n"
|
|
628
|
-
|
|
629
|
-
threads.each_with_index do |t,i|
|
|
630
|
-
$stdout.syswrite "#{pid}: Thread #{i+1}/#{total}: #{t.inspect}\n"
|
|
631
|
-
$stdout.syswrite "#{pid}: #{t.backtrace.join("\n#{pid}: ")}\n\n"
|
|
632
|
-
end
|
|
633
|
-
$stdout.syswrite "#{pid}: === End thread backtrace dump ===\n"
|
|
634
|
-
end
|
|
635
|
-
|
|
636
634
|
if @status != :restart
|
|
637
635
|
@binder.close
|
|
638
636
|
end
|
|
639
637
|
|
|
640
|
-
|
|
641
|
-
if timeout = options[:force_shutdown_after]
|
|
642
|
-
@thread_pool.shutdown timeout.to_f
|
|
643
|
-
else
|
|
644
|
-
@thread_pool.shutdown
|
|
645
|
-
end
|
|
646
|
-
end
|
|
638
|
+
@thread_pool.shutdown(options[:force_shutdown_after])
|
|
647
639
|
end
|
|
648
640
|
|
|
649
641
|
def notify_safely(message)
|
|
@@ -660,7 +652,7 @@ module Puma
|
|
|
660
652
|
end
|
|
661
653
|
private :notify_safely
|
|
662
654
|
|
|
663
|
-
# Stops the acceptor thread and then causes the
|
|
655
|
+
# Stops the acceptor thread and then causes the processor threads to finish
|
|
664
656
|
# off the request queue before finally exiting.
|
|
665
657
|
|
|
666
658
|
def stop(sync=false)
|
|
@@ -730,6 +722,49 @@ module Puma
|
|
|
730
722
|
@binder.add_unix_listener path, umask, mode, backlog
|
|
731
723
|
end
|
|
732
724
|
|
|
725
|
+
# Updates the minimum and maximum number of threads in the thread pool.
|
|
726
|
+
#
|
|
727
|
+
# This method allows dynamic adjustment of the thread pool size while the server
|
|
728
|
+
# is running. It validates the provided values and updates both the thread pool
|
|
729
|
+
# and the server's thread configuration.
|
|
730
|
+
#
|
|
731
|
+
# @param min [Integer] The minimum number of threads to maintain in the pool.
|
|
732
|
+
# Defaults to the current minimum if not specified. Must be greater than 0
|
|
733
|
+
# and less than or equal to max.
|
|
734
|
+
# @param max [Integer] The maximum number of threads allowed in the pool.
|
|
735
|
+
# Defaults to the current maximum if not specified. Must be greater than or
|
|
736
|
+
# equal to min.
|
|
737
|
+
#
|
|
738
|
+
# @return [void]
|
|
739
|
+
#
|
|
740
|
+
# @note If validation fails, a warning message is logged and no changes are made.
|
|
741
|
+
#
|
|
742
|
+
# @example Update both min and max threads
|
|
743
|
+
# server.update_thread_pool_min_max(min: 2, max: 8)
|
|
744
|
+
#
|
|
745
|
+
# @example Update only the minimum threads
|
|
746
|
+
# server.update_thread_pool_min_max(min: 4)
|
|
747
|
+
#
|
|
748
|
+
# @example Update only the maximum threads
|
|
749
|
+
# server.update_thread_pool_min_max(max: 16)
|
|
750
|
+
#
|
|
751
|
+
def update_thread_pool_min_max(min: @min_threads, max: @max_threads)
|
|
752
|
+
if min > max
|
|
753
|
+
@log_writer.log "`min' value cannot be greater than `max' value."
|
|
754
|
+
return
|
|
755
|
+
end
|
|
756
|
+
|
|
757
|
+
if min < 0
|
|
758
|
+
@log_writer.log "`min' value cannot be less than 0"
|
|
759
|
+
return
|
|
760
|
+
end
|
|
761
|
+
|
|
762
|
+
@thread_pool&.with_mutex do
|
|
763
|
+
@thread_pool.min, @thread_pool.max = min, max
|
|
764
|
+
@min_threads, @max_threads = min, max
|
|
765
|
+
end
|
|
766
|
+
end
|
|
767
|
+
|
|
733
768
|
# @!attribute [r] connected_ports
|
|
734
769
|
def connected_ports
|
|
735
770
|
@binder.connected_ports
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
module Puma
|
|
2
|
+
# ServerPluginControl provides a control interface for server plugins to
|
|
3
|
+
# interact with and manage server settings dynamically.
|
|
4
|
+
#
|
|
5
|
+
# This class acts as a facade between plugins and the Puma server,
|
|
6
|
+
# allowing plugins to safely modify server configuration and thread pool
|
|
7
|
+
# settings without direct access to the server's internal state.
|
|
8
|
+
#
|
|
9
|
+
class ServerPluginControl
|
|
10
|
+
def initialize(server)
|
|
11
|
+
@server = server
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# Returns the maximum number of threads in the thread pool.
|
|
15
|
+
def max_threads
|
|
16
|
+
@server.max_threads
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Returns the minimum number of threads in the thread pool.
|
|
20
|
+
def min_threads
|
|
21
|
+
@server.min_threads
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Updates the minimum and maximum number of threads in the thread pool.
|
|
25
|
+
#
|
|
26
|
+
# @see Puma::Server#update_thread_pool_min_max
|
|
27
|
+
#
|
|
28
|
+
def update_thread_pool_min_max(min: max_threads, max: min_threads)
|
|
29
|
+
@server.update_thread_pool_min_max(min: min, max: max)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
data/lib/puma/thread_pool.rb
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
require 'thread'
|
|
4
4
|
|
|
5
5
|
require_relative 'io_buffer'
|
|
6
|
+
require_relative 'server_plugin_control'
|
|
6
7
|
|
|
7
8
|
module Puma
|
|
8
9
|
|
|
@@ -24,6 +25,51 @@ module Puma
|
|
|
24
25
|
class ForceShutdown < RuntimeError
|
|
25
26
|
end
|
|
26
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
|
+
|
|
27
73
|
# How long, after raising the ForceShutdown of a thread during
|
|
28
74
|
# forced shutdown mode, to wait for the thread to try and finish
|
|
29
75
|
# up its work before leaving the thread to die on the vine.
|
|
@@ -52,10 +98,13 @@ module Puma
|
|
|
52
98
|
@name = name
|
|
53
99
|
@min = Integer(options[:min_threads])
|
|
54
100
|
@max = Integer(options[:max_threads])
|
|
101
|
+
@max_io_threads = Integer(options[:max_io_threads] || 0)
|
|
102
|
+
|
|
55
103
|
# Not an 'exposed' option, options[:pool_shutdown_grace_time] is used in CI
|
|
56
104
|
# to shorten @shutdown_grace_time from SHUTDOWN_GRACE_TIME. Parallel CI
|
|
57
105
|
# makes stubbing constants difficult.
|
|
58
106
|
@shutdown_grace_time = Float(options[:pool_shutdown_grace_time] || SHUTDOWN_GRACE_TIME)
|
|
107
|
+
@shutdown_debug = options[:shutdown_debug]
|
|
59
108
|
@block = block
|
|
60
109
|
@out_of_band = options[:out_of_band]
|
|
61
110
|
@out_of_band_running = false
|
|
@@ -70,7 +119,7 @@ module Puma
|
|
|
70
119
|
@trim_requested = 0
|
|
71
120
|
@out_of_band_pending = false
|
|
72
121
|
|
|
73
|
-
@
|
|
122
|
+
@processors = []
|
|
74
123
|
|
|
75
124
|
@auto_trim = nil
|
|
76
125
|
@reaper = nil
|
|
@@ -87,6 +136,7 @@ module Puma
|
|
|
87
136
|
end
|
|
88
137
|
|
|
89
138
|
attr_reader :spawned, :trim_requested, :waiting
|
|
139
|
+
attr_accessor :min, :max
|
|
90
140
|
|
|
91
141
|
# generate stats hash so as not to perform multiple locks
|
|
92
142
|
# @return [Hash] hash containing stat info from ThreadPool
|
|
@@ -96,8 +146,9 @@ module Puma
|
|
|
96
146
|
@backlog_max = 0
|
|
97
147
|
{ backlog: @todo.size,
|
|
98
148
|
running: @spawned,
|
|
99
|
-
pool_capacity:
|
|
149
|
+
pool_capacity: pool_capacity,
|
|
100
150
|
busy_threads: @spawned - @waiting + @todo.size,
|
|
151
|
+
io_threads: @processors.count(&:marked_as_io_thread?),
|
|
101
152
|
backlog_max: temp
|
|
102
153
|
}
|
|
103
154
|
end
|
|
@@ -121,7 +172,7 @@ module Puma
|
|
|
121
172
|
|
|
122
173
|
# @!attribute [r] pool_capacity
|
|
123
174
|
def pool_capacity
|
|
124
|
-
waiting + (@max - spawned)
|
|
175
|
+
(waiting + (@max - spawned)).clamp(0, Float::INFINITY)
|
|
125
176
|
end
|
|
126
177
|
|
|
127
178
|
# @!attribute [r] busy_threads
|
|
@@ -138,7 +189,8 @@ module Puma
|
|
|
138
189
|
@spawned += 1
|
|
139
190
|
|
|
140
191
|
trigger_before_thread_start_hooks
|
|
141
|
-
|
|
192
|
+
processor = ProcessorThread.new(self)
|
|
193
|
+
processor.thread = Thread.new(processor, @spawned) do |processor, spawned|
|
|
142
194
|
Puma.set_thread_name '%s tp %03i' % [@name, spawned]
|
|
143
195
|
# Advertise server into the thread
|
|
144
196
|
Thread.current.puma_server = @server
|
|
@@ -153,11 +205,23 @@ module Puma
|
|
|
153
205
|
work = nil
|
|
154
206
|
|
|
155
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
|
+
|
|
156
220
|
while todo.empty?
|
|
157
221
|
if @trim_requested > 0
|
|
158
222
|
@trim_requested -= 1
|
|
159
223
|
@spawned -= 1
|
|
160
|
-
@
|
|
224
|
+
@processors.delete(processor)
|
|
161
225
|
not_full.signal
|
|
162
226
|
trigger_before_thread_exit_hooks
|
|
163
227
|
Thread.exit
|
|
@@ -179,16 +243,16 @@ module Puma
|
|
|
179
243
|
end
|
|
180
244
|
|
|
181
245
|
begin
|
|
182
|
-
@out_of_band_pending = true if block.call(work)
|
|
246
|
+
@out_of_band_pending = true if block.call(processor, work)
|
|
183
247
|
rescue Exception => e
|
|
184
248
|
STDERR.puts "Error reached top of thread-pool: #{e.message} (#{e.class})"
|
|
185
249
|
end
|
|
186
250
|
end
|
|
187
251
|
end
|
|
188
252
|
|
|
189
|
-
@
|
|
253
|
+
@processors << processor
|
|
190
254
|
|
|
191
|
-
|
|
255
|
+
processor
|
|
192
256
|
end
|
|
193
257
|
|
|
194
258
|
private :spawn_thread
|
|
@@ -198,7 +262,7 @@ module Puma
|
|
|
198
262
|
|
|
199
263
|
@before_thread_start.each do |b|
|
|
200
264
|
begin
|
|
201
|
-
b[:block].call
|
|
265
|
+
b[:block].call(ServerPluginControl.new(@server))
|
|
202
266
|
rescue Exception => e
|
|
203
267
|
STDERR.puts "WARNING before_thread_start hook failed with exception (#{e.class}) #{e.message}"
|
|
204
268
|
end
|
|
@@ -257,6 +321,16 @@ module Puma
|
|
|
257
321
|
@mutex.synchronize(&block)
|
|
258
322
|
end
|
|
259
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
|
+
|
|
260
334
|
# Add +work+ to the todo list for a Thread to pickup and process.
|
|
261
335
|
def <<(work)
|
|
262
336
|
with_mutex do
|
|
@@ -268,12 +342,21 @@ module Puma
|
|
|
268
342
|
t = @todo.size
|
|
269
343
|
@backlog_max = t if t > @backlog_max
|
|
270
344
|
|
|
271
|
-
if @waiting < @todo.size and
|
|
345
|
+
if @waiting < @todo.size and can_spawn_processor?
|
|
272
346
|
spawn_thread
|
|
273
347
|
end
|
|
274
348
|
|
|
275
349
|
@not_empty.signal
|
|
276
350
|
end
|
|
351
|
+
self
|
|
352
|
+
end
|
|
353
|
+
|
|
354
|
+
def spawn_thread_if_needed # :nodoc:
|
|
355
|
+
with_mutex do
|
|
356
|
+
if @waiting < @todo.size and can_spawn_processor?
|
|
357
|
+
spawn_thread
|
|
358
|
+
end
|
|
359
|
+
end
|
|
277
360
|
end
|
|
278
361
|
|
|
279
362
|
# If there are any free threads in the pool, tell one to go ahead
|
|
@@ -294,16 +377,12 @@ module Puma
|
|
|
294
377
|
# spawned counter so that new healthy threads could be created again.
|
|
295
378
|
def reap
|
|
296
379
|
with_mutex do
|
|
297
|
-
|
|
380
|
+
@processors, dead_processors = @processors.partition(&:alive?)
|
|
298
381
|
|
|
299
|
-
|
|
300
|
-
|
|
382
|
+
dead_processors.each do |processor|
|
|
383
|
+
processor.kill
|
|
301
384
|
@spawned -= 1
|
|
302
385
|
end
|
|
303
|
-
|
|
304
|
-
@workers.delete_if do |w|
|
|
305
|
-
dead_workers.include?(w)
|
|
306
|
-
end
|
|
307
386
|
end
|
|
308
387
|
end
|
|
309
388
|
|
|
@@ -362,7 +441,7 @@ module Puma
|
|
|
362
441
|
# Next, wait an extra +@shutdown_grace_time+ seconds then force-kill remaining
|
|
363
442
|
# threads. Finally, wait 1 second for remaining threads to exit.
|
|
364
443
|
#
|
|
365
|
-
def shutdown(timeout
|
|
444
|
+
def shutdown(timeout)
|
|
366
445
|
threads = with_mutex do
|
|
367
446
|
@shutdown = true
|
|
368
447
|
@trim_requested = @spawned
|
|
@@ -371,8 +450,12 @@ module Puma
|
|
|
371
450
|
|
|
372
451
|
@auto_trim&.stop
|
|
373
452
|
@reaper&.stop
|
|
374
|
-
# dup
|
|
375
|
-
@
|
|
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")
|
|
376
459
|
end
|
|
377
460
|
|
|
378
461
|
if timeout == -1
|
|
@@ -382,13 +465,16 @@ module Puma
|
|
|
382
465
|
join = ->(inner_timeout) do
|
|
383
466
|
start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
384
467
|
threads.reject! do |t|
|
|
385
|
-
|
|
386
|
-
t.join
|
|
468
|
+
remaining = inner_timeout - (Process.clock_gettime(Process::CLOCK_MONOTONIC) - start)
|
|
469
|
+
remaining > 0 && t.join(remaining)
|
|
387
470
|
end
|
|
388
471
|
end
|
|
389
472
|
|
|
390
473
|
# Wait +timeout+ seconds for threads to finish.
|
|
391
474
|
join.call(timeout)
|
|
475
|
+
if @shutdown_debug == :on_force && !threads.empty?
|
|
476
|
+
shutdown_debug("Shutdown timeout exceeded")
|
|
477
|
+
end
|
|
392
478
|
|
|
393
479
|
# If threads are still running, raise ForceShutdown and wait to finish.
|
|
394
480
|
@shutdown_mutex.synchronize do
|
|
@@ -398,6 +484,9 @@ module Puma
|
|
|
398
484
|
end
|
|
399
485
|
end
|
|
400
486
|
join.call(@shutdown_grace_time)
|
|
487
|
+
if @shutdown_debug == :on_force && !threads.empty?
|
|
488
|
+
shutdown_debug("Shutdown grace timeout exceeded")
|
|
489
|
+
end
|
|
401
490
|
|
|
402
491
|
# If threads are _still_ running, forcefully kill them and wait to finish.
|
|
403
492
|
threads.each(&:kill)
|
|
@@ -405,7 +494,24 @@ module Puma
|
|
|
405
494
|
end
|
|
406
495
|
|
|
407
496
|
@spawned = 0
|
|
408
|
-
@
|
|
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"
|
|
409
515
|
end
|
|
410
516
|
end
|
|
411
517
|
end
|
data/lib/rack/handler/puma.rb
CHANGED
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:
|
|
4
|
+
version: 8.0.1
|
|
5
5
|
platform: java
|
|
6
6
|
authors:
|
|
7
7
|
- Evan Phoenix
|
|
@@ -42,13 +42,21 @@ files:
|
|
|
42
42
|
- bin/puma
|
|
43
43
|
- bin/puma-wild
|
|
44
44
|
- bin/pumactl
|
|
45
|
+
- docs/5.0-Upgrade.md
|
|
46
|
+
- docs/6.0-Upgrade.md
|
|
47
|
+
- docs/7.0-Upgrade.md
|
|
48
|
+
- docs/8.0-Upgrade.md
|
|
45
49
|
- docs/architecture.md
|
|
46
50
|
- docs/compile_options.md
|
|
47
51
|
- docs/deployment.md
|
|
48
52
|
- docs/fork_worker.md
|
|
53
|
+
- docs/grpc.md
|
|
54
|
+
- docs/images/favicon.svg
|
|
49
55
|
- docs/images/puma-connection-flow-no-reactor.png
|
|
50
56
|
- docs/images/puma-connection-flow.png
|
|
51
57
|
- docs/images/puma-general-arch.png
|
|
58
|
+
- docs/images/running-puma.svg
|
|
59
|
+
- docs/images/standard-logo.svg
|
|
52
60
|
- docs/java_options.md
|
|
53
61
|
- docs/jungle/README.md
|
|
54
62
|
- docs/jungle/rc.d/README.md
|
|
@@ -73,6 +81,7 @@ files:
|
|
|
73
81
|
- ext/puma_http11/http11_parser_common.rl
|
|
74
82
|
- ext/puma_http11/mini_ssl.c
|
|
75
83
|
- ext/puma_http11/no_ssl/PumaHttp11Service.java
|
|
84
|
+
- ext/puma_http11/org/jruby/puma/EnvKey.java
|
|
76
85
|
- ext/puma_http11/org/jruby/puma/Http11.java
|
|
77
86
|
- ext/puma_http11/org/jruby/puma/Http11Parser.java
|
|
78
87
|
- ext/puma_http11/org/jruby/puma/MiniSSL.java
|
|
@@ -82,6 +91,7 @@ files:
|
|
|
82
91
|
- lib/puma/binder.rb
|
|
83
92
|
- lib/puma/cli.rb
|
|
84
93
|
- lib/puma/client.rb
|
|
94
|
+
- lib/puma/client_env.rb
|
|
85
95
|
- lib/puma/cluster.rb
|
|
86
96
|
- lib/puma/cluster/worker.rb
|
|
87
97
|
- lib/puma/cluster/worker_handle.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
|
|
@@ -146,7 +157,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
146
157
|
- !ruby/object:Gem::Version
|
|
147
158
|
version: '0'
|
|
148
159
|
requirements: []
|
|
149
|
-
rubygems_version:
|
|
160
|
+
rubygems_version: 4.0.3
|
|
150
161
|
specification_version: 4
|
|
151
162
|
summary: A Ruby/Rack web server built for parallelism.
|
|
152
163
|
test_files: []
|