puma 4.1.0 → 4.2.1
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.
- checksums.yaml +4 -4
- data/History.md +44 -9
- data/README.md +7 -16
- data/docs/plugins.md +20 -10
- data/ext/puma_http11/http11_parser.c +37 -62
- data/ext/puma_http11/http11_parser_common.rl +3 -3
- data/lib/puma.rb +6 -0
- data/lib/puma/accept_nonblock.rb +5 -1
- data/lib/puma/app/status.rb +29 -28
- data/lib/puma/binder.rb +36 -12
- data/lib/puma/cli.rb +4 -0
- data/lib/puma/client.rb +194 -206
- data/lib/puma/cluster.rb +41 -45
- data/lib/puma/const.rb +15 -18
- data/lib/puma/control_cli.rb +11 -2
- data/lib/puma/dsl.rb +17 -1
- data/lib/puma/events.rb +2 -2
- data/lib/puma/launcher.rb +87 -46
- data/lib/puma/plugin.rb +5 -2
- data/lib/puma/reactor.rb +5 -4
- data/lib/puma/runner.rb +3 -3
- data/lib/puma/server.rb +14 -11
- data/lib/puma/thread_pool.rb +9 -31
- data/lib/rack/handler/puma.rb +0 -2
- data/tools/docker/Dockerfile +16 -0
- data/tools/trickletest.rb +0 -1
- metadata +4 -4
- data/lib/puma/daemon_ext.rb +0 -33
- data/lib/puma/delegation.rb +0 -13
data/lib/puma/cluster.rb
CHANGED
@@ -35,34 +35,10 @@ module Puma
|
|
35
35
|
@workers.each { |x| x.term }
|
36
36
|
|
37
37
|
begin
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
rescue Errno::ECHILD
|
43
|
-
# child is already terminated
|
44
|
-
end
|
45
|
-
end
|
46
|
-
else
|
47
|
-
# below code is for a bug in Ruby 2.6+, above waitpid call hangs
|
48
|
-
t_st = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
49
|
-
pids = @workers.map(&:pid)
|
50
|
-
loop do
|
51
|
-
pids.reject! do |w_pid|
|
52
|
-
begin
|
53
|
-
if Process.waitpid(w_pid, Process::WNOHANG)
|
54
|
-
log " worker status: #{$?}"
|
55
|
-
true
|
56
|
-
end
|
57
|
-
rescue Errno::ECHILD
|
58
|
-
true # child is already terminated
|
59
|
-
end
|
60
|
-
end
|
61
|
-
break if pids.empty?
|
62
|
-
sleep 0.5
|
63
|
-
end
|
64
|
-
t_end = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
65
|
-
log format(" worker shutdown time: %6.2f", t_end - t_st)
|
38
|
+
loop do
|
39
|
+
wait_workers
|
40
|
+
break if @workers.empty?
|
41
|
+
sleep 0.2
|
66
42
|
end
|
67
43
|
rescue Interrupt
|
68
44
|
log "! Cancelled waiting for workers"
|
@@ -98,7 +74,7 @@ module Puma
|
|
98
74
|
@started_at = Time.now
|
99
75
|
@last_checkin = Time.now
|
100
76
|
@last_status = '{}'
|
101
|
-
@
|
77
|
+
@term = false
|
102
78
|
end
|
103
79
|
|
104
80
|
attr_reader :index, :pid, :phase, :signal, :last_checkin, :last_status, :started_at
|
@@ -112,12 +88,8 @@ module Puma
|
|
112
88
|
@stage = :booted
|
113
89
|
end
|
114
90
|
|
115
|
-
def
|
116
|
-
@
|
117
|
-
end
|
118
|
-
|
119
|
-
def dead!
|
120
|
-
@dead = true
|
91
|
+
def term?
|
92
|
+
@term
|
121
93
|
end
|
122
94
|
|
123
95
|
def ping!(status)
|
@@ -134,9 +106,9 @@ module Puma
|
|
134
106
|
if @first_term_sent && (Time.now - @first_term_sent) > @options[:worker_shutdown_timeout]
|
135
107
|
@signal = "KILL"
|
136
108
|
else
|
109
|
+
@term ||= true
|
137
110
|
@first_term_sent ||= Time.now
|
138
111
|
end
|
139
|
-
|
140
112
|
Process.kill @signal, @pid
|
141
113
|
rescue Errno::ESRCH
|
142
114
|
end
|
@@ -227,12 +199,7 @@ module Puma
|
|
227
199
|
# during this loop by giving the kernel time to kill them.
|
228
200
|
sleep 1 if any
|
229
201
|
|
230
|
-
|
231
|
-
while pid = Process.waitpid(-1, Process::WNOHANG) do
|
232
|
-
pids << pid
|
233
|
-
end
|
234
|
-
@workers.reject! { |w| w.dead? || pids.include?(w.pid) }
|
235
|
-
|
202
|
+
wait_workers
|
236
203
|
cull_workers
|
237
204
|
spawn_workers
|
238
205
|
|
@@ -249,8 +216,10 @@ module Puma
|
|
249
216
|
log "- Stopping #{w.pid} for phased upgrade..."
|
250
217
|
end
|
251
218
|
|
252
|
-
w.term
|
253
|
-
|
219
|
+
unless w.term?
|
220
|
+
w.term
|
221
|
+
log "- #{w.signal} sent to #{w.pid}..."
|
222
|
+
end
|
254
223
|
end
|
255
224
|
end
|
256
225
|
end
|
@@ -277,6 +246,7 @@ module Puma
|
|
277
246
|
@suicide_pipe.close
|
278
247
|
|
279
248
|
Thread.new do
|
249
|
+
Puma.set_thread_name "worker check pipe"
|
280
250
|
IO.select [@check_pipe]
|
281
251
|
log "! Detected parent died, dying"
|
282
252
|
exit! 1
|
@@ -299,6 +269,7 @@ module Puma
|
|
299
269
|
server = start_server
|
300
270
|
|
301
271
|
Signal.trap "SIGTERM" do
|
272
|
+
@worker_write << "e#{Process.pid}\n" rescue nil
|
302
273
|
server.stop
|
303
274
|
end
|
304
275
|
|
@@ -311,6 +282,7 @@ module Puma
|
|
311
282
|
end
|
312
283
|
|
313
284
|
Thread.new(@worker_write) do |io|
|
285
|
+
Puma.set_thread_name "stat payload"
|
314
286
|
base_payload = "p#{Process.pid}"
|
315
287
|
|
316
288
|
while true
|
@@ -376,6 +348,8 @@ module Puma
|
|
376
348
|
Dir.chdir dir
|
377
349
|
end
|
378
350
|
|
351
|
+
# Inside of a child process, this will return all zeroes, as @workers is only populated in
|
352
|
+
# the master process.
|
379
353
|
def stats
|
380
354
|
old_worker_count = @workers.count { |w| w.phase != @phase }
|
381
355
|
booted_worker_count = @workers.count { |w| w.booted? }
|
@@ -530,8 +504,11 @@ module Puma
|
|
530
504
|
w.boot!
|
531
505
|
log "- Worker #{w.index} (pid: #{pid}) booted, phase: #{w.phase}"
|
532
506
|
force_check = true
|
507
|
+
when "e"
|
508
|
+
# external term, see worker method, Signal.trap "SIGTERM"
|
509
|
+
w.instance_variable_set :@term, true
|
533
510
|
when "t"
|
534
|
-
w.
|
511
|
+
w.term unless w.term?
|
535
512
|
force_check = true
|
536
513
|
when "p"
|
537
514
|
w.ping!(result.sub(/^\d+/,'').chomp)
|
@@ -554,5 +531,24 @@ module Puma
|
|
554
531
|
@wakeup.close
|
555
532
|
end
|
556
533
|
end
|
534
|
+
|
535
|
+
private
|
536
|
+
|
537
|
+
# loops thru @workers, removing workers that exited, and calling
|
538
|
+
# `#term` if needed
|
539
|
+
def wait_workers
|
540
|
+
@workers.reject! do |w|
|
541
|
+
begin
|
542
|
+
if Process.wait(w.pid, Process::WNOHANG)
|
543
|
+
true
|
544
|
+
else
|
545
|
+
w.term if w.term?
|
546
|
+
nil
|
547
|
+
end
|
548
|
+
rescue Errno::ECHILD
|
549
|
+
true # child is already terminated
|
550
|
+
end
|
551
|
+
end
|
552
|
+
end
|
557
553
|
end
|
558
554
|
end
|
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 = "4.1
|
104
|
-
CODE_NAME = "
|
103
|
+
PUMA_VERSION = VERSION = "4.2.1".freeze
|
104
|
+
CODE_NAME = "Distant Airhorns".freeze
|
105
105
|
PUMA_SERVER_STRING = ['puma', PUMA_VERSION, CODE_NAME].join(' ').freeze
|
106
106
|
|
107
107
|
FAST_TRACK_KA_TIMEOUT = 0.2
|
@@ -122,27 +122,24 @@ module Puma
|
|
122
122
|
REQUEST_URI= 'REQUEST_URI'.freeze
|
123
123
|
REQUEST_PATH = 'REQUEST_PATH'.freeze
|
124
124
|
QUERY_STRING = 'QUERY_STRING'.freeze
|
125
|
+
CONTENT_LENGTH = "CONTENT_LENGTH".freeze
|
125
126
|
|
126
127
|
PATH_INFO = 'PATH_INFO'.freeze
|
127
128
|
|
128
129
|
PUMA_TMP_BASE = "puma".freeze
|
129
130
|
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
ERROR_500_RESPONSE = "HTTP/1.1 500 Internal Server Error\r\n\r\n".freeze
|
143
|
-
|
144
|
-
# A common header for indicating the server is too busy. Not used yet.
|
145
|
-
ERROR_503_RESPONSE = "HTTP/1.1 503 Service Unavailable\r\n\r\nBUSY".freeze
|
131
|
+
ERROR_RESPONSE = {
|
132
|
+
# Indicate that we couldn't parse the request
|
133
|
+
400 => "HTTP/1.1 400 Bad Request\r\n\r\n".freeze,
|
134
|
+
# The standard empty 404 response for bad requests. Use Error4040Handler for custom stuff.
|
135
|
+
404 => "HTTP/1.1 404 Not Found\r\nConnection: close\r\nServer: Puma #{PUMA_VERSION}\r\n\r\nNOT FOUND".freeze,
|
136
|
+
# The standard empty 408 response for requests that timed out.
|
137
|
+
408 => "HTTP/1.1 408 Request Timeout\r\nConnection: close\r\nServer: Puma #{PUMA_VERSION}\r\n\r\n".freeze,
|
138
|
+
# Indicate that there was an internal error, obviously.
|
139
|
+
500 => "HTTP/1.1 500 Internal Server Error\r\n\r\n".freeze,
|
140
|
+
# A common header for indicating the server is too busy. Not used yet.
|
141
|
+
503 => "HTTP/1.1 503 Service Unavailable\r\n\r\nBUSY".freeze
|
142
|
+
}
|
146
143
|
|
147
144
|
# The basic max request size we'll try to read.
|
148
145
|
CHUNK_SIZE = 16 * 1024
|
data/lib/puma/control_cli.rb
CHANGED
@@ -22,6 +22,7 @@ module Puma
|
|
22
22
|
@control_auth_token = nil
|
23
23
|
@config_file = nil
|
24
24
|
@command = nil
|
25
|
+
@environment = ENV['RACK_ENV'] || "development"
|
25
26
|
|
26
27
|
@argv = argv.dup
|
27
28
|
@stdout = stdout
|
@@ -59,6 +60,11 @@ module Puma
|
|
59
60
|
@config_file = arg
|
60
61
|
end
|
61
62
|
|
63
|
+
o.on "-e", "--environment ENVIRONMENT",
|
64
|
+
"The environment to run the Rack app on (default development)" do |arg|
|
65
|
+
@environment = arg
|
66
|
+
end
|
67
|
+
|
62
68
|
o.on_tail("-H", "--help", "Show this message") do
|
63
69
|
@stdout.puts o
|
64
70
|
exit
|
@@ -76,8 +82,10 @@ module Puma
|
|
76
82
|
@command = argv.shift
|
77
83
|
|
78
84
|
unless @config_file == '-'
|
79
|
-
if @config_file.nil?
|
80
|
-
@config_file =
|
85
|
+
if @config_file.nil?
|
86
|
+
@config_file = %W(config/puma/#{@environment}.rb config/puma.rb).find do |f|
|
87
|
+
File.exist?(f)
|
88
|
+
end
|
81
89
|
end
|
82
90
|
|
83
91
|
if @config_file
|
@@ -258,6 +266,7 @@ module Puma
|
|
258
266
|
run_args += ["--control-url", @control_url] if @control_url
|
259
267
|
run_args += ["--control-token", @control_auth_token] if @control_auth_token
|
260
268
|
run_args += ["-C", @config_file] if @config_file
|
269
|
+
run_args += ["-e", @environment] if @environment
|
261
270
|
|
262
271
|
events = Puma::Events.new @stdout, @stderr
|
263
272
|
|
data/lib/puma/dsl.rb
CHANGED
@@ -396,7 +396,7 @@ module Puma
|
|
396
396
|
# keystore_pass: password
|
397
397
|
# }
|
398
398
|
def ssl_bind(host, port, opts)
|
399
|
-
verify = opts.fetch(:verify_mode, 'none')
|
399
|
+
verify = opts.fetch(:verify_mode, 'none').to_s
|
400
400
|
no_tlsv1 = opts.fetch(:no_tlsv1, 'false')
|
401
401
|
no_tlsv1_1 = opts.fetch(:no_tlsv1_1, 'false')
|
402
402
|
ca_additions = "&ca=#{opts[:ca]}" if ['peer', 'force_peer'].include?(verify)
|
@@ -584,6 +584,7 @@ module Puma
|
|
584
584
|
# dictates.
|
585
585
|
#
|
586
586
|
# @note This is incompatible with +preload_app!+.
|
587
|
+
# @note This is only supported for RubyGems 2.2+
|
587
588
|
def prune_bundler(answer=true)
|
588
589
|
@options[:prune_bundler] = answer
|
589
590
|
end
|
@@ -601,6 +602,21 @@ module Puma
|
|
601
602
|
@options[:raise_exception_on_sigterm] = answer
|
602
603
|
end
|
603
604
|
|
605
|
+
# When using prune_bundler, if extra runtime dependencies need to be loaded to
|
606
|
+
# initialize your app, then this setting can be used.
|
607
|
+
#
|
608
|
+
# Before bundler is pruned, the gem names supplied will be looked up in the bundler
|
609
|
+
# context and then loaded again after bundler is pruned.
|
610
|
+
# Only applies if prune_bundler is used.
|
611
|
+
#
|
612
|
+
# @example
|
613
|
+
# extra_runtime_dependencies ['gem_name_1', 'gem_name_2']
|
614
|
+
# @example
|
615
|
+
# extra_runtime_dependencies ['puma_worker_killer']
|
616
|
+
def extra_runtime_dependencies(answer = [])
|
617
|
+
@options[:extra_runtime_dependencies] = Array(answer)
|
618
|
+
end
|
619
|
+
|
604
620
|
# Additional text to display in process listing.
|
605
621
|
#
|
606
622
|
# If you do not specify a tag, Puma will infer it. If you do not want Puma
|
data/lib/puma/events.rb
CHANGED
data/lib/puma/launcher.rb
CHANGED
@@ -2,12 +2,9 @@
|
|
2
2
|
|
3
3
|
require 'puma/events'
|
4
4
|
require 'puma/detect'
|
5
|
-
|
6
5
|
require 'puma/cluster'
|
7
6
|
require 'puma/single'
|
8
|
-
|
9
7
|
require 'puma/const'
|
10
|
-
|
11
8
|
require 'puma/binder'
|
12
9
|
|
13
10
|
module Puma
|
@@ -126,19 +123,6 @@ module Puma
|
|
126
123
|
File.unlink(path) if path && File.exist?(path)
|
127
124
|
end
|
128
125
|
|
129
|
-
# If configured, write the pid of the current process out
|
130
|
-
# to a file.
|
131
|
-
def write_pid
|
132
|
-
path = @options[:pidfile]
|
133
|
-
return unless path
|
134
|
-
|
135
|
-
File.open(path, 'w') { |f| f.puts Process.pid }
|
136
|
-
cur = Process.pid
|
137
|
-
at_exit do
|
138
|
-
delete_pidfile if cur == Process.pid
|
139
|
-
end
|
140
|
-
end
|
141
|
-
|
142
126
|
# Begin async shutdown of the server
|
143
127
|
def halt
|
144
128
|
@status = :halt
|
@@ -200,6 +184,7 @@ module Puma
|
|
200
184
|
when :exit
|
201
185
|
# nothing
|
202
186
|
end
|
187
|
+
@binder.close_unix_paths
|
203
188
|
end
|
204
189
|
|
205
190
|
# Return which tcp port the launcher is using, if it's using TCP
|
@@ -217,16 +202,24 @@ module Puma
|
|
217
202
|
end
|
218
203
|
|
219
204
|
def close_binder_listeners
|
220
|
-
@binder.
|
221
|
-
io.close
|
222
|
-
uri = URI.parse(l)
|
223
|
-
next unless uri.scheme == 'unix'
|
224
|
-
File.unlink("#{uri.host}#{uri.path}")
|
225
|
-
end
|
205
|
+
@binder.close_listeners
|
226
206
|
end
|
227
207
|
|
228
208
|
private
|
229
209
|
|
210
|
+
# If configured, write the pid of the current process out
|
211
|
+
# to a file.
|
212
|
+
def write_pid
|
213
|
+
path = @options[:pidfile]
|
214
|
+
return unless path
|
215
|
+
|
216
|
+
File.open(path, 'w') { |f| f.puts Process.pid }
|
217
|
+
cur = Process.pid
|
218
|
+
at_exit do
|
219
|
+
delete_pidfile if cur == Process.pid
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
230
223
|
def reload_worker_directory
|
231
224
|
@runner.reload_worker_directory if @runner.respond_to?(:reload_worker_directory)
|
232
225
|
end
|
@@ -246,48 +239,71 @@ module Puma
|
|
246
239
|
Dir.chdir(@restart_dir)
|
247
240
|
Kernel.exec(*argv)
|
248
241
|
else
|
249
|
-
redirects = {:close_others => true}
|
250
|
-
@binder.listeners.each_with_index do |(l, io), i|
|
251
|
-
ENV["PUMA_INHERIT_#{i}"] = "#{io.to_i}:#{l}"
|
252
|
-
redirects[io.to_i] = io.to_i
|
253
|
-
end
|
254
|
-
|
255
242
|
argv = restart_args
|
256
243
|
Dir.chdir(@restart_dir)
|
257
|
-
argv += [
|
244
|
+
argv += [@binder.redirects_for_restart]
|
258
245
|
Kernel.exec(*argv)
|
259
246
|
end
|
260
247
|
end
|
261
248
|
|
262
|
-
def
|
263
|
-
|
264
|
-
|
265
|
-
|
249
|
+
def dependencies_and_files_to_require_after_prune
|
250
|
+
puma = spec_for_gem("puma")
|
251
|
+
|
252
|
+
deps = puma.runtime_dependencies.map do |d|
|
253
|
+
"#{d.name}:#{spec_for_gem(d.name).version}"
|
254
|
+
end
|
255
|
+
|
256
|
+
[deps, require_paths_for_gem(puma) + extra_runtime_deps_directories]
|
257
|
+
end
|
258
|
+
|
259
|
+
def extra_runtime_deps_directories
|
260
|
+
Array(@options[:extra_runtime_dependencies]).map do |d_name|
|
261
|
+
if (spec = spec_for_gem(d_name))
|
262
|
+
require_paths_for_gem(spec)
|
263
|
+
else
|
264
|
+
log "* Could not load extra dependency: #{d_name}"
|
265
|
+
nil
|
266
|
+
end
|
267
|
+
end.flatten.compact
|
268
|
+
end
|
269
|
+
|
270
|
+
def puma_wild_location
|
271
|
+
puma = spec_for_gem("puma")
|
272
|
+
dirs = require_paths_for_gem(puma)
|
266
273
|
puma_lib_dir = dirs.detect { |x| File.exist? File.join(x, '../bin/puma-wild') }
|
274
|
+
File.expand_path(File.join(puma_lib_dir, "../bin/puma-wild"))
|
275
|
+
end
|
267
276
|
|
268
|
-
|
277
|
+
def prune_bundler
|
278
|
+
return unless defined?(Bundler)
|
279
|
+
require_rubygems_min_version!(Gem::Version.new("2.2"), "prune_bundler")
|
280
|
+
unless puma_wild_location
|
269
281
|
log "! Unable to prune Bundler environment, continuing"
|
270
282
|
return
|
271
283
|
end
|
272
284
|
|
273
|
-
deps =
|
274
|
-
spec = Bundler.rubygems.loaded_specs(d.name)
|
275
|
-
"#{d.name}:#{spec.version.to_s}"
|
276
|
-
end
|
285
|
+
deps, dirs = dependencies_and_files_to_require_after_prune
|
277
286
|
|
278
287
|
log '* Pruning Bundler environment'
|
279
288
|
home = ENV['GEM_HOME']
|
280
289
|
Bundler.with_clean_env do
|
281
290
|
ENV['GEM_HOME'] = home
|
282
291
|
ENV['PUMA_BUNDLER_PRUNED'] = '1'
|
283
|
-
|
284
|
-
args = [Gem.ruby, wild, '-I', dirs.join(':'), deps.join(',')] + @original_argv
|
292
|
+
args = [Gem.ruby, puma_wild_location, '-I', dirs.join(':'), deps.join(',')] + @original_argv
|
285
293
|
# Ruby 2.0+ defaults to true which breaks socket activation
|
286
294
|
args += [{:close_others => false}]
|
287
295
|
Kernel.exec(*args)
|
288
296
|
end
|
289
297
|
end
|
290
298
|
|
299
|
+
def spec_for_gem(gem_name)
|
300
|
+
Bundler.rubygems.loaded_specs(gem_name)
|
301
|
+
end
|
302
|
+
|
303
|
+
def require_paths_for_gem(gem_spec)
|
304
|
+
gem_spec.full_require_paths
|
305
|
+
end
|
306
|
+
|
291
307
|
def log(str)
|
292
308
|
@events.log str
|
293
309
|
end
|
@@ -307,6 +323,21 @@ module Puma
|
|
307
323
|
log "- Goodbye!"
|
308
324
|
end
|
309
325
|
|
326
|
+
def log_thread_status
|
327
|
+
Thread.list.each do |thread|
|
328
|
+
log "Thread TID-#{thread.object_id.to_s(36)} #{thread['label']}"
|
329
|
+
logstr = "Thread: TID-#{thread.object_id.to_s(36)}"
|
330
|
+
logstr += " #{thread.name}" if thread.respond_to?(:name)
|
331
|
+
log logstr
|
332
|
+
|
333
|
+
if thread.backtrace
|
334
|
+
log thread.backtrace.join("\n")
|
335
|
+
else
|
336
|
+
log "<no backtrace available>"
|
337
|
+
end
|
338
|
+
end
|
339
|
+
end
|
340
|
+
|
310
341
|
def set_process_title
|
311
342
|
Process.respond_to?(:setproctitle) ? Process.setproctitle(title) : $0 = title
|
312
343
|
end
|
@@ -406,12 +437,6 @@ module Puma
|
|
406
437
|
|
407
438
|
begin
|
408
439
|
Signal.trap "SIGINT" do
|
409
|
-
if Puma.jruby?
|
410
|
-
@status = :exit
|
411
|
-
graceful_stop
|
412
|
-
exit
|
413
|
-
end
|
414
|
-
|
415
440
|
stop
|
416
441
|
end
|
417
442
|
rescue Exception
|
@@ -429,6 +454,22 @@ module Puma
|
|
429
454
|
rescue Exception
|
430
455
|
log "*** SIGHUP not implemented, signal based logs reopening unavailable!"
|
431
456
|
end
|
457
|
+
|
458
|
+
begin
|
459
|
+
Signal.trap "SIGINFO" do
|
460
|
+
log_thread_status
|
461
|
+
end
|
462
|
+
rescue Exception
|
463
|
+
# Not going to log this one, as SIGINFO is *BSD only and would be pretty annoying
|
464
|
+
# to see this constantly on Linux.
|
465
|
+
end
|
466
|
+
end
|
467
|
+
|
468
|
+
def require_rubygems_min_version!(min_version, feature)
|
469
|
+
return if min_version <= Gem::Version.new(Gem::VERSION)
|
470
|
+
|
471
|
+
raise "#{feature} is not supported on your version of RubyGems. " \
|
472
|
+
"You must have RubyGems #{min_version}+ to use this feature."
|
432
473
|
end
|
433
474
|
end
|
434
475
|
end
|