puma 4.3.6-java → 5.0.2-java

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of puma might be problematic. Click here for more details.

Files changed (64) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +1153 -518
  3. data/LICENSE +23 -20
  4. data/README.md +26 -13
  5. data/docs/architecture.md +3 -3
  6. data/docs/deployment.md +9 -3
  7. data/docs/fork_worker.md +31 -0
  8. data/docs/jungle/README.md +13 -0
  9. data/{tools → docs}/jungle/rc.d/README.md +0 -0
  10. data/{tools → docs}/jungle/rc.d/puma +0 -0
  11. data/{tools → docs}/jungle/rc.d/puma.conf +0 -0
  12. data/{tools → docs}/jungle/upstart/README.md +0 -0
  13. data/{tools → docs}/jungle/upstart/puma-manager.conf +0 -0
  14. data/{tools → docs}/jungle/upstart/puma.conf +0 -0
  15. data/docs/signals.md +7 -6
  16. data/docs/systemd.md +1 -63
  17. data/ext/puma_http11/PumaHttp11Service.java +2 -4
  18. data/ext/puma_http11/extconf.rb +4 -3
  19. data/ext/puma_http11/mini_ssl.c +15 -2
  20. data/ext/puma_http11/no_ssl/PumaHttp11Service.java +15 -0
  21. data/ext/puma_http11/org/jruby/puma/Http11.java +3 -3
  22. data/ext/puma_http11/org/jruby/puma/MiniSSL.java +77 -18
  23. data/ext/puma_http11/puma_http11.c +6 -38
  24. data/lib/puma.rb +20 -0
  25. data/lib/puma/app/status.rb +14 -1
  26. data/lib/puma/binder.rb +90 -68
  27. data/lib/puma/cli.rb +7 -15
  28. data/lib/puma/client.rb +62 -13
  29. data/lib/puma/cluster.rb +193 -74
  30. data/lib/puma/commonlogger.rb +2 -2
  31. data/lib/puma/configuration.rb +31 -42
  32. data/lib/puma/const.rb +3 -3
  33. data/lib/puma/control_cli.rb +29 -17
  34. data/lib/puma/detect.rb +17 -0
  35. data/lib/puma/dsl.rb +144 -70
  36. data/lib/puma/error_logger.rb +97 -0
  37. data/lib/puma/events.rb +37 -31
  38. data/lib/puma/io_buffer.rb +9 -2
  39. data/lib/puma/jruby_restart.rb +0 -58
  40. data/lib/puma/launcher.rb +57 -31
  41. data/lib/puma/minissl.rb +68 -18
  42. data/lib/puma/minissl/context_builder.rb +0 -3
  43. data/lib/puma/null_io.rb +1 -1
  44. data/lib/puma/plugin.rb +1 -10
  45. data/lib/puma/puma_http11.jar +0 -0
  46. data/lib/puma/rack/builder.rb +0 -4
  47. data/lib/puma/reactor.rb +10 -16
  48. data/lib/puma/runner.rb +8 -36
  49. data/lib/puma/server.rb +161 -218
  50. data/lib/puma/single.rb +8 -64
  51. data/lib/puma/state_file.rb +6 -3
  52. data/lib/puma/thread_pool.rb +116 -51
  53. data/lib/puma/util.rb +1 -0
  54. data/lib/rack/handler/puma.rb +1 -3
  55. data/tools/{docker/Dockerfile → Dockerfile} +0 -0
  56. metadata +17 -19
  57. data/docs/tcp_mode.md +0 -96
  58. data/ext/puma_http11/io_buffer.c +0 -155
  59. data/ext/puma_http11/org/jruby/puma/IOBuffer.java +0 -72
  60. data/lib/puma/tcp_logger.rb +0 -41
  61. data/tools/jungle/README.md +0 -19
  62. data/tools/jungle/init.d/README.md +0 -61
  63. data/tools/jungle/init.d/puma +0 -421
  64. data/tools/jungle/init.d/run-puma +0 -18
@@ -13,12 +13,11 @@ module Puma
13
13
  # gets created via the `start_server` method from the `Puma::Runner` class
14
14
  # that this inherits from.
15
15
  class Single < Runner
16
+ # @!attribute [r] stats
16
17
  def stats
17
- b = @server.backlog || 0
18
- r = @server.running || 0
19
- t = @server.pool_capacity || 0
20
- m = @server.max_threads || 0
21
- %Q!{ "started_at": "#{@started_at.utc.iso8601}", "backlog": #{b}, "running": #{r}, "pool_capacity": #{t}, "max_threads": #{m} }!
18
+ {
19
+ started_at: @started_at.utc.iso8601
20
+ }.merge(@server.stats)
22
21
  end
23
22
 
24
23
  def restart
@@ -39,64 +38,10 @@ module Puma
39
38
  @server.stop(true) if @server
40
39
  end
41
40
 
42
- def jruby_daemon?
43
- daemon? and Puma.jruby?
44
- end
45
-
46
- def jruby_daemon_start
47
- require 'puma/jruby_restart'
48
- JRubyRestart.daemon_start(@restart_dir, @launcher.restart_args)
49
- end
50
-
51
41
  def run
52
- already_daemon = false
53
-
54
- if jruby_daemon?
55
- require 'puma/jruby_restart'
56
-
57
- if JRubyRestart.daemon?
58
- # load and bind before redirecting IO so errors show up on stdout/stderr
59
- load_and_bind
60
- redirect_io
61
- end
62
-
63
- already_daemon = JRubyRestart.daemon_init
64
- end
65
-
66
42
  output_header "single"
67
43
 
68
- if jruby_daemon?
69
- if already_daemon
70
- JRubyRestart.perm_daemonize
71
- else
72
- pid = nil
73
-
74
- Signal.trap "SIGUSR2" do
75
- log "* Started new process #{pid} as daemon..."
76
-
77
- # Must use exit! so we don't unwind and run the ensures
78
- # that will be run by the new child (such as deleting the
79
- # pidfile)
80
- exit!(true)
81
- end
82
-
83
- Signal.trap "SIGCHLD" do
84
- log "! Error starting new process as daemon, exiting"
85
- exit 1
86
- end
87
-
88
- jruby_daemon_start
89
- sleep
90
- end
91
- else
92
- if daemon?
93
- log "* Daemonizing..."
94
- Process.daemon(true)
95
- redirect_io
96
- end
97
-
98
- load_and_bind
99
- end
44
+ load_and_bind
100
45
 
101
46
  Plugins.fire_background
102
47
 
@@ -106,10 +51,9 @@ module Puma
106
51
 
107
52
  @server = server = start_server
108
53
 
109
- unless daemon?
110
- log "Use Ctrl-C to stop"
111
- redirect_io
112
- end
54
+
55
+ log "Use Ctrl-C to stop"
56
+ redirect_io
113
57
 
114
58
  @launcher.events.fire_on_booted!
115
59
 
@@ -8,15 +8,18 @@ module Puma
8
8
  @options = {}
9
9
  end
10
10
 
11
- def save(path)
12
- File.write path, YAML.dump(@options)
11
+ def save(path, permission = nil)
12
+ File.open(path, "w") do |file|
13
+ file.chmod(permission) if permission
14
+ file.write(YAML.dump(@options))
15
+ end
13
16
  end
14
17
 
15
18
  def load(path)
16
19
  @options = YAML.load File.read(path)
17
20
  end
18
21
 
19
- FIELDS = %w!control_url control_auth_token pid!
22
+ FIELDS = %w!control_url control_auth_token pid running_from!
20
23
 
21
24
  FIELDS.each do |f|
22
25
  define_method f do
@@ -47,6 +47,7 @@ module Puma
47
47
  @shutdown = false
48
48
 
49
49
  @trim_requested = 0
50
+ @out_of_band_pending = false
50
51
 
51
52
  @workers = []
52
53
 
@@ -54,14 +55,20 @@ module Puma
54
55
  @reaper = nil
55
56
 
56
57
  @mutex.synchronize do
57
- @min.times { spawn_thread }
58
+ @min.times do
59
+ spawn_thread
60
+ @not_full.wait(@mutex)
61
+ end
58
62
  end
59
63
 
60
64
  @clean_thread_locals = false
65
+ @force_shutdown = false
66
+ @shutdown_mutex = Mutex.new
61
67
  end
62
68
 
63
69
  attr_reader :spawned, :trim_requested, :waiting
64
70
  attr_accessor :clean_thread_locals
71
+ attr_accessor :out_of_band_hook # @version 5.0.0
65
72
 
66
73
  def self.clean_thread_locals
67
74
  Thread.current.keys.each do |key| # rubocop: disable Performance/HashEachMethods
@@ -72,13 +79,20 @@ module Puma
72
79
  # How many objects have yet to be processed by the pool?
73
80
  #
74
81
  def backlog
75
- @mutex.synchronize { @todo.size }
82
+ with_mutex { @todo.size }
76
83
  end
77
84
 
85
+ # @!attribute [r] pool_capacity
78
86
  def pool_capacity
79
87
  waiting + (@max - spawned)
80
88
  end
81
89
 
90
+ # @!attribute [r] busy_threads
91
+ # @version 5.0.0
92
+ def busy_threads
93
+ with_mutex { @spawned - @waiting + @todo.size }
94
+ end
95
+
82
96
  # :nodoc:
83
97
  #
84
98
  # Must be called with @mutex held!
@@ -99,48 +113,40 @@ module Puma
99
113
  while true
100
114
  work = nil
101
115
 
102
- continue = true
103
-
104
116
  mutex.synchronize do
105
117
  while todo.empty?
106
118
  if @trim_requested > 0
107
119
  @trim_requested -= 1
108
- continue = false
109
- not_full.signal
110
- break
111
- end
112
-
113
- if @shutdown
114
- continue = false
115
- break
120
+ @spawned -= 1
121
+ @workers.delete th
122
+ Thread.exit
116
123
  end
117
124
 
118
125
  @waiting += 1
126
+ if @out_of_band_pending && trigger_out_of_band_hook
127
+ @out_of_band_pending = false
128
+ end
119
129
  not_full.signal
120
- not_empty.wait mutex
121
- @waiting -= 1
130
+ begin
131
+ not_empty.wait mutex
132
+ ensure
133
+ @waiting -= 1
134
+ end
122
135
  end
123
136
 
124
- work = todo.shift if continue
137
+ work = todo.shift
125
138
  end
126
139
 
127
- break unless continue
128
-
129
140
  if @clean_thread_locals
130
141
  ThreadPool.clean_thread_locals
131
142
  end
132
143
 
133
144
  begin
134
- block.call(work, *extra)
145
+ @out_of_band_pending = true if block.call(work, *extra)
135
146
  rescue Exception => e
136
147
  STDERR.puts "Error reached top of thread-pool: #{e.message} (#{e.class})"
137
148
  end
138
149
  end
139
-
140
- mutex.synchronize do
141
- @spawned -= 1
142
- @workers.delete th
143
- end
144
150
  end
145
151
 
146
152
  @workers << th
@@ -150,9 +156,32 @@ module Puma
150
156
 
151
157
  private :spawn_thread
152
158
 
159
+ # @version 5.0.0
160
+ def trigger_out_of_band_hook
161
+ return false unless out_of_band_hook && out_of_band_hook.any?
162
+
163
+ # we execute on idle hook when all threads are free
164
+ return false unless @spawned == @waiting
165
+
166
+ out_of_band_hook.each(&:call)
167
+ true
168
+ rescue Exception => e
169
+ STDERR.puts "Exception calling out_of_band_hook: #{e.message} (#{e.class})"
170
+ true
171
+ end
172
+
173
+ private :trigger_out_of_band_hook
174
+
175
+ # @version 5.0.0
176
+ def with_mutex(&block)
177
+ @mutex.owned? ?
178
+ yield :
179
+ @mutex.synchronize(&block)
180
+ end
181
+
153
182
  # Add +work+ to the todo list for a Thread to pickup and process.
154
183
  def <<(work)
155
- @mutex.synchronize do
184
+ with_mutex do
156
185
  if @shutdown
157
186
  raise "Unable to add work while shutting down"
158
187
  end
@@ -193,11 +222,8 @@ module Puma
193
222
  # method would not block and another request would be added into the reactor
194
223
  # by the server. This would continue until a fully bufferend request
195
224
  # makes it through the reactor and can then be processed by the thread pool.
196
- #
197
- # Returns the current number of busy threads, or +nil+ if shutting down.
198
- #
199
225
  def wait_until_not_full
200
- @mutex.synchronize do
226
+ with_mutex do
201
227
  while true
202
228
  return if @shutdown
203
229
 
@@ -205,21 +231,41 @@ module Puma
205
231
  # is work queued that cannot be handled by waiting
206
232
  # threads, then accept more work until we would
207
233
  # spin up the max number of threads.
208
- busy_threads = @spawned - @waiting + @todo.size
209
- return busy_threads if @max > busy_threads
234
+ return if busy_threads < @max
210
235
 
211
236
  @not_full.wait @mutex
212
237
  end
213
238
  end
214
239
  end
215
240
 
216
- # If too many threads are in the pool, tell one to finish go ahead
241
+ # @version 5.0.0
242
+ def wait_for_less_busy_worker(delay_s)
243
+ # Ruby MRI does GVL, this can result
244
+ # in processing contention when multiple threads
245
+ # (requests) are running concurrently
246
+ return unless Puma.mri?
247
+ return unless delay_s > 0
248
+
249
+ with_mutex do
250
+ return if @shutdown
251
+
252
+ # do not delay, if we are not busy
253
+ return unless busy_threads > 0
254
+
255
+ # this will be signaled once a request finishes,
256
+ # which can happen earlier than delay
257
+ @not_full.wait @mutex, delay_s
258
+ end
259
+ end
260
+
261
+ # If there are any free threads in the pool, tell one to go ahead
217
262
  # and exit. If +force+ is true, then a trim request is requested
218
263
  # even if all threads are being utilized.
219
264
  #
220
265
  def trim(force=false)
221
- @mutex.synchronize do
222
- if (force or @waiting > 0) and @spawned - @trim_requested > @min
266
+ with_mutex do
267
+ free = @waiting - @todo.size
268
+ if (force or free > 0) and @spawned - @trim_requested > @min
223
269
  @trim_requested += 1
224
270
  @not_empty.signal
225
271
  end
@@ -229,7 +275,7 @@ module Puma
229
275
  # If there are dead threads in the pool make them go away while decreasing
230
276
  # spawned counter so that new healthy threads could be created again.
231
277
  def reap
232
- @mutex.synchronize do
278
+ with_mutex do
233
279
  dead_workers = @workers.reject(&:alive?)
234
280
 
235
281
  dead_workers.each do |worker|
@@ -280,11 +326,28 @@ module Puma
280
326
  @reaper.start!
281
327
  end
282
328
 
329
+ # Allows ThreadPool::ForceShutdown to be raised within the
330
+ # provided block if the thread is forced to shutdown during execution.
331
+ def with_force_shutdown
332
+ t = Thread.current
333
+ @shutdown_mutex.synchronize do
334
+ raise ForceShutdown if @force_shutdown
335
+ t[:with_force_shutdown] = true
336
+ end
337
+ yield
338
+ ensure
339
+ t[:with_force_shutdown] = false
340
+ end
341
+
283
342
  # Tell all threads in the pool to exit and wait for them to finish.
343
+ # Wait +timeout+ seconds then raise +ForceShutdown+ in remaining threads.
344
+ # Next, wait an extra +grace+ seconds then force-kill remaining threads.
345
+ # Finally, wait +kill_grace+ seconds for remaining threads to exit.
284
346
  #
285
347
  def shutdown(timeout=-1)
286
- threads = @mutex.synchronize do
348
+ threads = with_mutex do
287
349
  @shutdown = true
350
+ @trim_requested = @spawned
288
351
  @not_empty.broadcast
289
352
  @not_full.broadcast
290
353
 
@@ -298,27 +361,29 @@ module Puma
298
361
  # Wait for threads to finish without force shutdown.
299
362
  threads.each(&:join)
300
363
  else
301
- # Wait for threads to finish after n attempts (+timeout+).
302
- # If threads are still running, it will forcefully kill them.
303
- timeout.times do
304
- threads.delete_if do |t|
305
- t.join 1
306
- end
307
-
308
- if threads.empty?
309
- break
310
- else
311
- sleep 1
364
+ join = ->(inner_timeout) do
365
+ start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
366
+ threads.reject! do |t|
367
+ elapsed = Process.clock_gettime(Process::CLOCK_MONOTONIC) - start
368
+ t.join inner_timeout - elapsed
312
369
  end
313
370
  end
314
371
 
315
- threads.each do |t|
316
- t.raise ForceShutdown
317
- end
372
+ # Wait +timeout+ seconds for threads to finish.
373
+ join.call(timeout)
318
374
 
319
- threads.each do |t|
320
- t.join SHUTDOWN_GRACE_TIME
375
+ # If threads are still running, raise ForceShutdown and wait to finish.
376
+ @shutdown_mutex.synchronize do
377
+ @force_shutdown = true
378
+ threads.each do |t|
379
+ t.raise ForceShutdown if t[:with_force_shutdown]
380
+ end
321
381
  end
382
+ join.call(SHUTDOWN_GRACE_TIME)
383
+
384
+ # If threads are _still_ running, forcefully kill them and wait to finish.
385
+ threads.each(&:kill)
386
+ join.call(1)
322
387
  end
323
388
 
324
389
  @spawned = 0
@@ -72,6 +72,7 @@ module Puma
72
72
  end
73
73
  end
74
74
 
75
+ # @!attribute [r] to_hash
75
76
  def to_hash
76
77
  hash = {}
77
78
  each { |k,v| hash[k] = v }
@@ -30,8 +30,6 @@ module Rack
30
30
  end
31
31
 
32
32
  conf = ::Puma::Configuration.new(options, default_options) do |user_config, file_config, default_config|
33
- user_config.quiet
34
-
35
33
  if options.delete(:Verbose)
36
34
  app = Rack::CommonLogger.new(app, STDOUT)
37
35
  end
@@ -61,7 +59,7 @@ module Rack
61
59
  conf
62
60
  end
63
61
 
64
- def self.run(app, options = {})
62
+ def self.run(app, **options)
65
63
  conf = self.config(app, options)
66
64
 
67
65
  events = options.delete(:Silent) ? ::Puma::Events.strings : ::Puma::Events.stdio
File without changes
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: puma
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.3.6
4
+ version: 5.0.2
5
5
  platform: java
6
6
  authors:
7
7
  - Evan Phoenix
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-09-05 00:00:00.000000000 Z
11
+ date: 2020-09-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  requirement: !ruby/object:Gem::Requirement
@@ -44,15 +44,22 @@ files:
44
44
  - bin/pumactl
45
45
  - docs/architecture.md
46
46
  - docs/deployment.md
47
+ - docs/fork_worker.md
47
48
  - docs/images/puma-connection-flow-no-reactor.png
48
49
  - docs/images/puma-connection-flow.png
49
50
  - docs/images/puma-general-arch.png
51
+ - docs/jungle/README.md
52
+ - docs/jungle/rc.d/README.md
53
+ - docs/jungle/rc.d/puma
54
+ - docs/jungle/rc.d/puma.conf
55
+ - docs/jungle/upstart/README.md
56
+ - docs/jungle/upstart/puma-manager.conf
57
+ - docs/jungle/upstart/puma.conf
50
58
  - docs/nginx.md
51
59
  - docs/plugins.md
52
60
  - docs/restart.md
53
61
  - docs/signals.md
54
62
  - docs/systemd.md
55
- - docs/tcp_mode.md
56
63
  - ext/puma_http11/PumaHttp11Service.java
57
64
  - ext/puma_http11/ext_help.h
58
65
  - ext/puma_http11/extconf.rb
@@ -61,11 +68,10 @@ files:
61
68
  - ext/puma_http11/http11_parser.java.rl
62
69
  - ext/puma_http11/http11_parser.rl
63
70
  - ext/puma_http11/http11_parser_common.rl
64
- - ext/puma_http11/io_buffer.c
65
71
  - ext/puma_http11/mini_ssl.c
72
+ - ext/puma_http11/no_ssl/PumaHttp11Service.java
66
73
  - ext/puma_http11/org/jruby/puma/Http11.java
67
74
  - ext/puma_http11/org/jruby/puma/Http11Parser.java
68
- - ext/puma_http11/org/jruby/puma/IOBuffer.java
69
75
  - ext/puma_http11/org/jruby/puma/MiniSSL.java
70
76
  - ext/puma_http11/puma_http11.c
71
77
  - lib/puma.rb
@@ -81,6 +87,7 @@ files:
81
87
  - lib/puma/control_cli.rb
82
88
  - lib/puma/detect.rb
83
89
  - lib/puma/dsl.rb
90
+ - lib/puma/error_logger.rb
84
91
  - lib/puma/events.rb
85
92
  - lib/puma/io_buffer.rb
86
93
  - lib/puma/jruby_restart.rb
@@ -99,28 +106,19 @@ files:
99
106
  - lib/puma/server.rb
100
107
  - lib/puma/single.rb
101
108
  - lib/puma/state_file.rb
102
- - lib/puma/tcp_logger.rb
103
109
  - lib/puma/thread_pool.rb
104
110
  - lib/puma/util.rb
105
111
  - lib/rack/handler/puma.rb
106
- - tools/docker/Dockerfile
107
- - tools/jungle/README.md
108
- - tools/jungle/init.d/README.md
109
- - tools/jungle/init.d/puma
110
- - tools/jungle/init.d/run-puma
111
- - tools/jungle/rc.d/README.md
112
- - tools/jungle/rc.d/puma
113
- - tools/jungle/rc.d/puma.conf
114
- - tools/jungle/upstart/README.md
115
- - tools/jungle/upstart/puma-manager.conf
116
- - tools/jungle/upstart/puma.conf
112
+ - tools/Dockerfile
117
113
  - tools/trickletest.rb
118
- homepage: http://puma.io
114
+ homepage: https://puma.io
119
115
  licenses:
120
116
  - BSD-3-Clause
121
117
  metadata:
122
- msys2_mingw_dependencies: openssl
118
+ bug_tracker_uri: https://github.com/puma/puma/issues
123
119
  changelog_uri: https://github.com/puma/puma/blob/master/History.md
120
+ homepage_uri: https://puma.io
121
+ source_code_uri: https://github.com/puma/puma
124
122
  post_install_message:
125
123
  rdoc_options: []
126
124
  require_paths: