puma-simon 3.7.1
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 +7 -0
- data/.github/issue_template.md +20 -0
- data/.gitignore +18 -0
- data/.hoeignore +12 -0
- data/.travis.yml +29 -0
- data/DEPLOYMENT.md +91 -0
- data/Gemfile +12 -0
- data/History.md +1254 -0
- data/LICENSE +26 -0
- data/Manifest.txt +78 -0
- data/README.md +353 -0
- data/Rakefile +158 -0
- data/Release.md +9 -0
- data/bin/puma +10 -0
- data/bin/puma-wild +31 -0
- data/bin/pumactl +12 -0
- data/docs/nginx.md +80 -0
- data/docs/signals.md +43 -0
- data/docs/systemd.md +197 -0
- data/examples/CA/cacert.pem +23 -0
- data/examples/CA/newcerts/cert_1.pem +19 -0
- data/examples/CA/newcerts/cert_2.pem +19 -0
- data/examples/CA/private/cakeypair.pem +30 -0
- data/examples/CA/serial +1 -0
- data/examples/config.rb +200 -0
- data/examples/plugins/redis_stop_puma.rb +46 -0
- data/examples/puma/cert_puma.pem +19 -0
- data/examples/puma/client-certs/ca.crt +19 -0
- data/examples/puma/client-certs/ca.key +27 -0
- data/examples/puma/client-certs/client.crt +19 -0
- data/examples/puma/client-certs/client.key +27 -0
- data/examples/puma/client-certs/client_expired.crt +19 -0
- data/examples/puma/client-certs/client_expired.key +27 -0
- data/examples/puma/client-certs/client_unknown.crt +19 -0
- data/examples/puma/client-certs/client_unknown.key +27 -0
- data/examples/puma/client-certs/generate.rb +78 -0
- data/examples/puma/client-certs/keystore.jks +0 -0
- data/examples/puma/client-certs/server.crt +19 -0
- data/examples/puma/client-certs/server.key +27 -0
- data/examples/puma/client-certs/server.p12 +0 -0
- data/examples/puma/client-certs/unknown_ca.crt +19 -0
- data/examples/puma/client-certs/unknown_ca.key +27 -0
- data/examples/puma/csr_puma.pem +11 -0
- data/examples/puma/keystore.jks +0 -0
- data/examples/puma/puma_keypair.pem +15 -0
- data/examples/qc_config.rb +13 -0
- data/ext/puma_http11/PumaHttp11Service.java +17 -0
- data/ext/puma_http11/ext_help.h +15 -0
- data/ext/puma_http11/extconf.rb +15 -0
- data/ext/puma_http11/http11_parser.c +1069 -0
- data/ext/puma_http11/http11_parser.h +65 -0
- data/ext/puma_http11/http11_parser.java.rl +161 -0
- data/ext/puma_http11/http11_parser.rl +147 -0
- data/ext/puma_http11/http11_parser_common.rl +54 -0
- data/ext/puma_http11/io_buffer.c +155 -0
- data/ext/puma_http11/mini_ssl.c +457 -0
- data/ext/puma_http11/org/jruby/puma/Http11.java +234 -0
- data/ext/puma_http11/org/jruby/puma/Http11Parser.java +473 -0
- data/ext/puma_http11/org/jruby/puma/MiniSSL.java +339 -0
- data/ext/puma_http11/puma_http11.c +500 -0
- data/gemfiles/2.1-Gemfile +12 -0
- data/lib/puma.rb +15 -0
- data/lib/puma/accept_nonblock.rb +23 -0
- data/lib/puma/app/status.rb +66 -0
- data/lib/puma/binder.rb +402 -0
- data/lib/puma/cli.rb +220 -0
- data/lib/puma/client.rb +434 -0
- data/lib/puma/cluster.rb +510 -0
- data/lib/puma/commonlogger.rb +106 -0
- data/lib/puma/compat.rb +14 -0
- data/lib/puma/configuration.rb +364 -0
- data/lib/puma/const.rb +224 -0
- data/lib/puma/control_cli.rb +259 -0
- data/lib/puma/convenient.rb +23 -0
- data/lib/puma/daemon_ext.rb +31 -0
- data/lib/puma/delegation.rb +11 -0
- data/lib/puma/detect.rb +13 -0
- data/lib/puma/dsl.rb +486 -0
- data/lib/puma/events.rb +152 -0
- data/lib/puma/io_buffer.rb +7 -0
- data/lib/puma/java_io_buffer.rb +45 -0
- data/lib/puma/jruby_restart.rb +83 -0
- data/lib/puma/launcher.rb +410 -0
- data/lib/puma/minissl.rb +221 -0
- data/lib/puma/null_io.rb +42 -0
- data/lib/puma/plugin.rb +115 -0
- data/lib/puma/plugin/tmp_restart.rb +35 -0
- data/lib/puma/rack/backports/uri/common_193.rb +33 -0
- data/lib/puma/rack/builder.rb +298 -0
- data/lib/puma/rack/urlmap.rb +91 -0
- data/lib/puma/rack_default.rb +7 -0
- data/lib/puma/reactor.rb +210 -0
- data/lib/puma/runner.rb +171 -0
- data/lib/puma/server.rb +949 -0
- data/lib/puma/single.rb +112 -0
- data/lib/puma/state_file.rb +29 -0
- data/lib/puma/tcp_logger.rb +39 -0
- data/lib/puma/thread_pool.rb +297 -0
- data/lib/puma/util.rb +128 -0
- data/lib/rack/handler/puma.rb +78 -0
- data/puma.gemspec +52 -0
- data/test/ab_rs.rb +22 -0
- data/test/config.rb +2 -0
- data/test/config/app.rb +9 -0
- data/test/config/plugin.rb +1 -0
- data/test/config/settings.rb +2 -0
- data/test/config/state_file_testing_config.rb +14 -0
- data/test/hello-bind.ru +2 -0
- data/test/hello-delay.ru +3 -0
- data/test/hello-map.ru +3 -0
- data/test/hello-post.ru +4 -0
- data/test/hello-stuck.ru +1 -0
- data/test/hello-tcp.ru +5 -0
- data/test/hello.ru +1 -0
- data/test/hijack.ru +6 -0
- data/test/hijack2.ru +5 -0
- data/test/lobster.ru +4 -0
- data/test/shell/run.sh +24 -0
- data/test/shell/t1.rb +19 -0
- data/test/shell/t1_conf.rb +3 -0
- data/test/shell/t2.rb +17 -0
- data/test/shell/t2_conf.rb +6 -0
- data/test/shell/t3.rb +25 -0
- data/test/shell/t3_conf.rb +5 -0
- data/test/slow.ru +4 -0
- data/test/ssl_config.rb +4 -0
- data/test/test_app_status.rb +93 -0
- data/test/test_binder.rb +31 -0
- data/test/test_cli.rb +209 -0
- data/test/test_config.rb +95 -0
- data/test/test_events.rb +161 -0
- data/test/test_helper.rb +50 -0
- data/test/test_http10.rb +27 -0
- data/test/test_http11.rb +186 -0
- data/test/test_integration.rb +247 -0
- data/test/test_iobuffer.rb +39 -0
- data/test/test_minissl.rb +29 -0
- data/test/test_null_io.rb +49 -0
- data/test/test_persistent.rb +245 -0
- data/test/test_puma_server.rb +626 -0
- data/test/test_puma_server_ssl.rb +222 -0
- data/test/test_rack_handler.rb +57 -0
- data/test/test_rack_server.rb +138 -0
- data/test/test_tcp_logger.rb +39 -0
- data/test/test_tcp_rack.rb +36 -0
- data/test/test_thread_pool.rb +250 -0
- data/test/test_unix_socket.rb +35 -0
- data/test/test_web_server.rb +88 -0
- data/tools/jungle/README.md +9 -0
- data/tools/jungle/init.d/README.md +59 -0
- data/tools/jungle/init.d/puma +421 -0
- data/tools/jungle/init.d/run-puma +18 -0
- data/tools/jungle/upstart/README.md +61 -0
- data/tools/jungle/upstart/puma-manager.conf +31 -0
- data/tools/jungle/upstart/puma.conf +69 -0
- data/tools/trickletest.rb +45 -0
- metadata +297 -0
data/lib/puma/single.rb
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
require 'puma/runner'
|
|
2
|
+
require 'puma/detect'
|
|
3
|
+
require 'puma/plugin'
|
|
4
|
+
|
|
5
|
+
module Puma
|
|
6
|
+
class Single < Runner
|
|
7
|
+
def stats
|
|
8
|
+
b = @server.backlog
|
|
9
|
+
r = @server.running
|
|
10
|
+
%Q!{ "backlog": #{b}, "running": #{r} }!
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def restart
|
|
14
|
+
@server.begin_restart
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def stop
|
|
18
|
+
@server.stop false
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def halt
|
|
22
|
+
@server.halt
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def stop_blocked
|
|
26
|
+
log "- Gracefully stopping, waiting for requests to finish"
|
|
27
|
+
@control.stop(true) if @control
|
|
28
|
+
@server.stop(true)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def jruby_daemon?
|
|
32
|
+
daemon? and Puma.jruby?
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def jruby_daemon_start
|
|
36
|
+
require 'puma/jruby_restart'
|
|
37
|
+
JRubyRestart.daemon_start(@restart_dir, @launcher.restart_args)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def run
|
|
41
|
+
already_daemon = false
|
|
42
|
+
|
|
43
|
+
if jruby_daemon?
|
|
44
|
+
require 'puma/jruby_restart'
|
|
45
|
+
|
|
46
|
+
if JRubyRestart.daemon?
|
|
47
|
+
# load and bind before redirecting IO so errors show up on stdout/stderr
|
|
48
|
+
load_and_bind
|
|
49
|
+
redirect_io
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
already_daemon = JRubyRestart.daemon_init
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
output_header "single"
|
|
56
|
+
|
|
57
|
+
if jruby_daemon?
|
|
58
|
+
if already_daemon
|
|
59
|
+
JRubyRestart.perm_daemonize
|
|
60
|
+
else
|
|
61
|
+
pid = nil
|
|
62
|
+
|
|
63
|
+
Signal.trap "SIGUSR2" do
|
|
64
|
+
log "* Started new process #{pid} as daemon..."
|
|
65
|
+
|
|
66
|
+
# Must use exit! so we don't unwind and run the ensures
|
|
67
|
+
# that will be run by the new child (such as deleting the
|
|
68
|
+
# pidfile)
|
|
69
|
+
exit!(true)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
Signal.trap "SIGCHLD" do
|
|
73
|
+
log "! Error starting new process as daemon, exiting"
|
|
74
|
+
exit 1
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
jruby_daemon_start
|
|
78
|
+
sleep
|
|
79
|
+
end
|
|
80
|
+
else
|
|
81
|
+
if daemon?
|
|
82
|
+
log "* Daemonizing..."
|
|
83
|
+
Process.daemon(true)
|
|
84
|
+
redirect_io
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
load_and_bind
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
Plugins.fire_background
|
|
91
|
+
|
|
92
|
+
@launcher.write_state
|
|
93
|
+
|
|
94
|
+
start_control
|
|
95
|
+
|
|
96
|
+
@server = server = start_server
|
|
97
|
+
|
|
98
|
+
unless daemon?
|
|
99
|
+
log "Use Ctrl-C to stop"
|
|
100
|
+
redirect_io
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
@launcher.events.fire_on_booted!
|
|
104
|
+
|
|
105
|
+
begin
|
|
106
|
+
server.run.join
|
|
107
|
+
rescue Interrupt
|
|
108
|
+
# Swallow it
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
require 'yaml'
|
|
2
|
+
|
|
3
|
+
module Puma
|
|
4
|
+
class StateFile
|
|
5
|
+
def initialize
|
|
6
|
+
@options = {}
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def save(path)
|
|
10
|
+
File.write path, YAML.dump(@options)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def load(path)
|
|
14
|
+
@options = YAML.load File.read(path)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
FIELDS = %w!control_url control_auth_token pid!
|
|
18
|
+
|
|
19
|
+
FIELDS.each do |f|
|
|
20
|
+
define_method f do
|
|
21
|
+
@options[f]
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
define_method "#{f}=" do |v|
|
|
25
|
+
@options[f] = v
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
module Puma
|
|
2
|
+
class TCPLogger
|
|
3
|
+
def initialize(logger, app, quiet=false)
|
|
4
|
+
@logger = logger
|
|
5
|
+
@app = app
|
|
6
|
+
@quiet = quiet
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
FORMAT = "%s - %s"
|
|
10
|
+
|
|
11
|
+
def log(who, str)
|
|
12
|
+
now = Time.now.strftime("%d/%b/%Y %H:%M:%S")
|
|
13
|
+
|
|
14
|
+
log_str = "#{now} - #{who} - #{str}"
|
|
15
|
+
|
|
16
|
+
case @logger
|
|
17
|
+
when IO
|
|
18
|
+
@logger.puts log_str
|
|
19
|
+
when Events
|
|
20
|
+
@logger.log log_str
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def call(env, socket)
|
|
25
|
+
who = env[Const::REMOTE_ADDR]
|
|
26
|
+
log who, "connected" unless @quiet
|
|
27
|
+
|
|
28
|
+
env['log'] = lambda { |str| log(who, str) }
|
|
29
|
+
|
|
30
|
+
begin
|
|
31
|
+
@app.call env, socket
|
|
32
|
+
rescue Object => e
|
|
33
|
+
log who, "exception: #{e.message} (#{e.class})"
|
|
34
|
+
else
|
|
35
|
+
log who, "disconnected" unless @quiet
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
require 'thread'
|
|
2
|
+
|
|
3
|
+
module Puma
|
|
4
|
+
# A simple thread pool management object.
|
|
5
|
+
#
|
|
6
|
+
class ThreadPool
|
|
7
|
+
class ForceShutdown < RuntimeError
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
# How long, after raising the ForceShutdown of a thread during
|
|
11
|
+
# forced shutdown mode, to wait for the thread to try and finish
|
|
12
|
+
# up its work before leaving the thread to die on the vine.
|
|
13
|
+
SHUTDOWN_GRACE_TIME = 5 # seconds
|
|
14
|
+
|
|
15
|
+
# Maintain a minimum of +min+ and maximum of +max+ threads
|
|
16
|
+
# in the pool.
|
|
17
|
+
#
|
|
18
|
+
# The block passed is the work that will be performed in each
|
|
19
|
+
# thread.
|
|
20
|
+
#
|
|
21
|
+
def initialize(min, max, *extra, &block)
|
|
22
|
+
@not_empty = ConditionVariable.new
|
|
23
|
+
@not_full = ConditionVariable.new
|
|
24
|
+
@mutex = Mutex.new
|
|
25
|
+
|
|
26
|
+
@todo = []
|
|
27
|
+
|
|
28
|
+
@spawned = 0
|
|
29
|
+
@waiting = 0
|
|
30
|
+
|
|
31
|
+
@min = Integer(min)
|
|
32
|
+
@max = Integer(max)
|
|
33
|
+
@block = block
|
|
34
|
+
@extra = extra
|
|
35
|
+
|
|
36
|
+
@shutdown = false
|
|
37
|
+
|
|
38
|
+
@trim_requested = 0
|
|
39
|
+
|
|
40
|
+
@workers = []
|
|
41
|
+
|
|
42
|
+
@auto_trim = nil
|
|
43
|
+
@reaper = nil
|
|
44
|
+
|
|
45
|
+
@mutex.synchronize do
|
|
46
|
+
@min.times { spawn_thread }
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
@clean_thread_locals = false
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
attr_reader :spawned, :trim_requested
|
|
53
|
+
attr_accessor :clean_thread_locals
|
|
54
|
+
|
|
55
|
+
def self.clean_thread_locals
|
|
56
|
+
Thread.current.keys.each do |key|
|
|
57
|
+
Thread.current[key] = nil unless key == :__recursive_key__
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# How many objects have yet to be processed by the pool?
|
|
62
|
+
#
|
|
63
|
+
def backlog
|
|
64
|
+
@mutex.synchronize { @todo.size }
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# :nodoc:
|
|
68
|
+
#
|
|
69
|
+
# Must be called with @mutex held!
|
|
70
|
+
#
|
|
71
|
+
def spawn_thread
|
|
72
|
+
@spawned += 1
|
|
73
|
+
|
|
74
|
+
th = Thread.new do
|
|
75
|
+
# Thread name is new in Ruby 2.3
|
|
76
|
+
Thread.current.name = 'puma %03i' % @spawned if Thread.current.respond_to?(:name=)
|
|
77
|
+
todo = @todo
|
|
78
|
+
block = @block
|
|
79
|
+
mutex = @mutex
|
|
80
|
+
not_empty = @not_empty
|
|
81
|
+
not_full = @not_full
|
|
82
|
+
|
|
83
|
+
extra = @extra.map { |i| i.new }
|
|
84
|
+
|
|
85
|
+
while true
|
|
86
|
+
work = nil
|
|
87
|
+
|
|
88
|
+
continue = true
|
|
89
|
+
|
|
90
|
+
mutex.synchronize do
|
|
91
|
+
while todo.empty?
|
|
92
|
+
if @trim_requested > 0
|
|
93
|
+
@trim_requested -= 1
|
|
94
|
+
continue = false
|
|
95
|
+
not_full.signal
|
|
96
|
+
break
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
if @shutdown
|
|
100
|
+
continue = false
|
|
101
|
+
break
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
@waiting += 1
|
|
105
|
+
not_full.signal
|
|
106
|
+
not_empty.wait mutex
|
|
107
|
+
@waiting -= 1
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
work = todo.shift if continue
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
break unless continue
|
|
114
|
+
|
|
115
|
+
if @clean_thread_locals
|
|
116
|
+
ThreadPool.clean_thread_locals
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
begin
|
|
120
|
+
block.call(work, *extra)
|
|
121
|
+
rescue Exception => e
|
|
122
|
+
STDERR.puts "Error reached top of thread-pool: #{e.message} (#{e.class})"
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
mutex.synchronize do
|
|
127
|
+
@spawned -= 1
|
|
128
|
+
@workers.delete th
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
@workers << th
|
|
133
|
+
|
|
134
|
+
th
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
private :spawn_thread
|
|
138
|
+
|
|
139
|
+
# Add +work+ to the todo list for a Thread to pickup and process.
|
|
140
|
+
def <<(work)
|
|
141
|
+
@mutex.synchronize do
|
|
142
|
+
if @shutdown
|
|
143
|
+
raise "Unable to add work while shutting down"
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
@todo << work
|
|
147
|
+
|
|
148
|
+
if @waiting < @todo.size and @spawned < @max
|
|
149
|
+
spawn_thread
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
@not_empty.signal
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def wait_until_not_full
|
|
157
|
+
@mutex.synchronize do
|
|
158
|
+
until @todo.size - @waiting < @max - @spawned or @shutdown
|
|
159
|
+
@not_full.wait @mutex
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
# If too many threads are in the pool, tell one to finish go ahead
|
|
165
|
+
# and exit. If +force+ is true, then a trim request is requested
|
|
166
|
+
# even if all threads are being utilized.
|
|
167
|
+
#
|
|
168
|
+
def trim(force=false)
|
|
169
|
+
@mutex.synchronize do
|
|
170
|
+
if (force or @waiting > 0) and @spawned - @trim_requested > @min
|
|
171
|
+
@trim_requested += 1
|
|
172
|
+
@not_empty.signal
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
# If there are dead threads in the pool make them go away while decreasing
|
|
178
|
+
# spawned counter so that new healthy threads could be created again.
|
|
179
|
+
def reap
|
|
180
|
+
@mutex.synchronize do
|
|
181
|
+
dead_workers = @workers.reject(&:alive?)
|
|
182
|
+
|
|
183
|
+
dead_workers.each do |worker|
|
|
184
|
+
worker.kill
|
|
185
|
+
@spawned -= 1
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
@workers.delete_if do |w|
|
|
189
|
+
dead_workers.include?(w)
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
class AutoTrim
|
|
195
|
+
def initialize(pool, timeout)
|
|
196
|
+
@pool = pool
|
|
197
|
+
@timeout = timeout
|
|
198
|
+
@running = false
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
def start!
|
|
202
|
+
@running = true
|
|
203
|
+
|
|
204
|
+
@thread = Thread.new do
|
|
205
|
+
while @running
|
|
206
|
+
@pool.trim
|
|
207
|
+
sleep @timeout
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
def stop
|
|
213
|
+
@running = false
|
|
214
|
+
@thread.wakeup
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
def auto_trim!(timeout=30)
|
|
219
|
+
@auto_trim = AutoTrim.new(self, timeout)
|
|
220
|
+
@auto_trim.start!
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
class Reaper
|
|
224
|
+
def initialize(pool, timeout)
|
|
225
|
+
@pool = pool
|
|
226
|
+
@timeout = timeout
|
|
227
|
+
@running = false
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
def start!
|
|
231
|
+
@running = true
|
|
232
|
+
|
|
233
|
+
@thread = Thread.new do
|
|
234
|
+
while @running
|
|
235
|
+
@pool.reap
|
|
236
|
+
sleep @timeout
|
|
237
|
+
end
|
|
238
|
+
end
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
def stop
|
|
242
|
+
@running = false
|
|
243
|
+
@thread.wakeup
|
|
244
|
+
end
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
def auto_reap!(timeout=5)
|
|
248
|
+
@reaper = Reaper.new(self, timeout)
|
|
249
|
+
@reaper.start!
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
# Tell all threads in the pool to exit and wait for them to finish.
|
|
253
|
+
#
|
|
254
|
+
def shutdown(timeout=-1)
|
|
255
|
+
threads = @mutex.synchronize do
|
|
256
|
+
@shutdown = true
|
|
257
|
+
@not_empty.broadcast
|
|
258
|
+
@not_full.broadcast
|
|
259
|
+
|
|
260
|
+
@auto_trim.stop if @auto_trim
|
|
261
|
+
@reaper.stop if @reaper
|
|
262
|
+
# dup workers so that we join them all safely
|
|
263
|
+
@workers.dup
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
if timeout == -1
|
|
267
|
+
# Wait for threads to finish without force shutdown.
|
|
268
|
+
threads.each(&:join)
|
|
269
|
+
else
|
|
270
|
+
# Wait for threads to finish after n attempts (+timeout+).
|
|
271
|
+
# If threads are still running, it will forcefully kill them.
|
|
272
|
+
timeout.times do
|
|
273
|
+
threads.delete_if do |t|
|
|
274
|
+
t.join 1
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
if threads.empty?
|
|
278
|
+
break
|
|
279
|
+
else
|
|
280
|
+
sleep 1
|
|
281
|
+
end
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
threads.each do |t|
|
|
285
|
+
t.raise ForceShutdown
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
threads.each do |t|
|
|
289
|
+
t.join SHUTDOWN_GRACE_TIME
|
|
290
|
+
end
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
@spawned = 0
|
|
294
|
+
@workers = []
|
|
295
|
+
end
|
|
296
|
+
end
|
|
297
|
+
end
|