puma 3.9.1 → 4.3.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 +5 -5
- data/History.md +232 -0
- data/README.md +162 -224
- data/docs/architecture.md +37 -0
- data/{DEPLOYMENT.md → docs/deployment.md} +24 -4
- data/docs/images/puma-connection-flow-no-reactor.png +0 -0
- data/docs/images/puma-connection-flow.png +0 -0
- data/docs/images/puma-general-arch.png +0 -0
- data/docs/plugins.md +38 -0
- data/docs/restart.md +41 -0
- data/docs/signals.md +56 -3
- data/docs/systemd.md +130 -37
- data/docs/tcp_mode.md +96 -0
- data/ext/puma_http11/PumaHttp11Service.java +2 -0
- data/ext/puma_http11/extconf.rb +13 -0
- data/ext/puma_http11/http11_parser.c +115 -140
- data/ext/puma_http11/http11_parser.java.rl +21 -37
- data/ext/puma_http11/http11_parser.rl +9 -9
- data/ext/puma_http11/http11_parser_common.rl +3 -3
- data/ext/puma_http11/mini_ssl.c +104 -8
- data/ext/puma_http11/org/jruby/puma/Http11.java +106 -114
- data/ext/puma_http11/org/jruby/puma/Http11Parser.java +90 -108
- data/ext/puma_http11/org/jruby/puma/IOBuffer.java +72 -0
- data/ext/puma_http11/org/jruby/puma/MiniSSL.java +21 -4
- data/ext/puma_http11/puma_http11.c +2 -0
- data/lib/puma.rb +16 -0
- data/lib/puma/accept_nonblock.rb +7 -1
- data/lib/puma/app/status.rb +40 -26
- data/lib/puma/binder.rb +57 -74
- data/lib/puma/cli.rb +26 -7
- data/lib/puma/client.rb +243 -190
- data/lib/puma/cluster.rb +78 -34
- data/lib/puma/commonlogger.rb +2 -0
- data/lib/puma/configuration.rb +24 -16
- data/lib/puma/const.rb +36 -18
- data/lib/puma/control_cli.rb +46 -19
- data/lib/puma/detect.rb +2 -0
- data/lib/puma/dsl.rb +329 -68
- data/lib/puma/events.rb +6 -1
- data/lib/puma/io_buffer.rb +3 -6
- data/lib/puma/jruby_restart.rb +2 -1
- data/lib/puma/launcher.rb +120 -58
- data/lib/puma/minissl.rb +69 -27
- data/lib/puma/minissl/context_builder.rb +76 -0
- data/lib/puma/null_io.rb +2 -0
- data/lib/puma/plugin.rb +7 -2
- data/lib/puma/plugin/tmp_restart.rb +2 -1
- data/lib/puma/rack/builder.rb +4 -1
- data/lib/puma/rack/urlmap.rb +2 -0
- data/lib/puma/rack_default.rb +2 -0
- data/lib/puma/reactor.rb +224 -34
- data/lib/puma/runner.rb +25 -4
- data/lib/puma/server.rb +148 -62
- data/lib/puma/single.rb +16 -5
- data/lib/puma/state_file.rb +2 -0
- data/lib/puma/tcp_logger.rb +2 -0
- data/lib/puma/thread_pool.rb +61 -38
- data/lib/puma/util.rb +2 -6
- data/lib/rack/handler/puma.rb +10 -4
- data/tools/docker/Dockerfile +16 -0
- data/tools/jungle/README.md +12 -2
- data/tools/jungle/init.d/README.md +2 -0
- data/tools/jungle/init.d/puma +8 -8
- data/tools/jungle/init.d/run-puma +1 -1
- data/tools/jungle/rc.d/README.md +74 -0
- data/tools/jungle/rc.d/puma +61 -0
- data/tools/jungle/rc.d/puma.conf +10 -0
- data/tools/trickletest.rb +1 -2
- metadata +29 -56
- data/.github/issue_template.md +0 -20
- data/Gemfile +0 -14
- data/Manifest.txt +0 -78
- data/Rakefile +0 -165
- data/Release.md +0 -9
- data/gemfiles/2.1-Gemfile +0 -12
- data/lib/puma/compat.rb +0 -14
- data/lib/puma/convenient.rb +0 -23
- data/lib/puma/daemon_ext.rb +0 -31
- data/lib/puma/delegation.rb +0 -11
- data/lib/puma/java_io_buffer.rb +0 -45
- data/lib/puma/rack/backports/uri/common_193.rb +0 -33
- data/puma.gemspec +0 -20
data/lib/puma/runner.rb
CHANGED
@@ -1,7 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'puma/server'
|
2
4
|
require 'puma/const'
|
5
|
+
require 'puma/minissl/context_builder'
|
3
6
|
|
4
7
|
module Puma
|
8
|
+
# Generic class that is used by `Puma::Cluster` and `Puma::Single` to
|
9
|
+
# serve requests. This class spawns a new instance of `Puma::Server` via
|
10
|
+
# a call to `start_server`.
|
5
11
|
class Runner
|
6
12
|
def initialize(cli, events)
|
7
13
|
@launcher = cli
|
@@ -9,6 +15,7 @@ module Puma
|
|
9
15
|
@options = cli.options
|
10
16
|
@app = nil
|
11
17
|
@control = nil
|
18
|
+
@started_at = Time.now
|
12
19
|
end
|
13
20
|
|
14
21
|
def daemon?
|
@@ -19,6 +26,10 @@ module Puma
|
|
19
26
|
@options[:environment] == "development"
|
20
27
|
end
|
21
28
|
|
29
|
+
def test?
|
30
|
+
@options[:environment] == "test"
|
31
|
+
end
|
32
|
+
|
22
33
|
def log(str)
|
23
34
|
@events.log str
|
24
35
|
end
|
@@ -43,17 +54,23 @@ module Puma
|
|
43
54
|
|
44
55
|
uri = URI.parse str
|
45
56
|
|
46
|
-
app = Puma::App::Status.new @launcher
|
47
|
-
|
48
57
|
if token = @options[:control_auth_token]
|
49
|
-
|
58
|
+
token = nil if token.empty? || token == 'none'
|
50
59
|
end
|
51
60
|
|
61
|
+
app = Puma::App::Status.new @launcher, token
|
62
|
+
|
52
63
|
control = Puma::Server.new app, @launcher.events
|
53
64
|
control.min_threads = 0
|
54
65
|
control.max_threads = 1
|
55
66
|
|
56
67
|
case uri.scheme
|
68
|
+
when "ssl"
|
69
|
+
log "* Starting control server on #{str}"
|
70
|
+
params = Util.parse_query uri.query
|
71
|
+
ctx = MiniSSL::ContextBuilder.new(params, @events).context
|
72
|
+
|
73
|
+
control.add_ssl_listener uri.host, uri.port, ctx
|
57
74
|
when "tcp"
|
58
75
|
log "* Starting control server on #{str}"
|
59
76
|
control.add_tcp_listener uri.host, uri.port
|
@@ -161,7 +178,11 @@ module Puma
|
|
161
178
|
server.tcp_mode!
|
162
179
|
end
|
163
180
|
|
164
|
-
|
181
|
+
if @options[:early_hints]
|
182
|
+
server.early_hints = true
|
183
|
+
end
|
184
|
+
|
185
|
+
unless development? || test?
|
165
186
|
server.leak_stack_on_error = false
|
166
187
|
end
|
167
188
|
|
data/lib/puma/server.rb
CHANGED
@@ -1,32 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'stringio'
|
2
4
|
|
3
5
|
require 'puma/thread_pool'
|
4
6
|
require 'puma/const'
|
5
7
|
require 'puma/events'
|
6
8
|
require 'puma/null_io'
|
7
|
-
require 'puma/compat'
|
8
9
|
require 'puma/reactor'
|
9
10
|
require 'puma/client'
|
10
11
|
require 'puma/binder'
|
11
|
-
require 'puma/delegation'
|
12
12
|
require 'puma/accept_nonblock'
|
13
13
|
require 'puma/util'
|
14
14
|
|
15
15
|
require 'puma/puma_http11'
|
16
16
|
|
17
|
-
unless Puma.const_defined? "IOBuffer"
|
18
|
-
require 'puma/io_buffer'
|
19
|
-
end
|
20
|
-
|
21
17
|
require 'socket'
|
18
|
+
require 'forwardable'
|
22
19
|
|
23
20
|
module Puma
|
24
21
|
|
25
22
|
# The HTTP Server itself. Serves out a single Rack app.
|
23
|
+
#
|
24
|
+
# This class is used by the `Puma::Single` and `Puma::Cluster` classes
|
25
|
+
# to generate one or more `Puma::Server` instances capable of handling requests.
|
26
|
+
# Each Puma process will contain one `Puma::Server` instance.
|
27
|
+
#
|
28
|
+
# The `Puma::Server` instance pulls requests from the socket, adds them to a
|
29
|
+
# `Puma::Reactor` where they get eventually passed to a `Puma::ThreadPool`.
|
30
|
+
#
|
31
|
+
# Each `Puma::Server` will have one reactor and one thread pool.
|
26
32
|
class Server
|
27
33
|
|
28
34
|
include Puma::Const
|
29
|
-
extend
|
35
|
+
extend Forwardable
|
30
36
|
|
31
37
|
attr_reader :thread
|
32
38
|
attr_reader :events
|
@@ -62,13 +68,12 @@ module Puma
|
|
62
68
|
|
63
69
|
@thread = nil
|
64
70
|
@thread_pool = nil
|
71
|
+
@early_hints = nil
|
65
72
|
|
66
73
|
@persistent_timeout = options.fetch(:persistent_timeout, PERSISTENT_TIMEOUT)
|
74
|
+
@first_data_timeout = options.fetch(:first_data_timeout, FIRST_DATA_TIMEOUT)
|
67
75
|
|
68
76
|
@binder = Binder.new(events)
|
69
|
-
@own_binder = true
|
70
|
-
|
71
|
-
@first_data_timeout = FIRST_DATA_TIMEOUT
|
72
77
|
|
73
78
|
@leak_stack_on_error = true
|
74
79
|
|
@@ -82,16 +87,12 @@ module Puma
|
|
82
87
|
@precheck_closing = true
|
83
88
|
end
|
84
89
|
|
85
|
-
attr_accessor :binder, :leak_stack_on_error
|
90
|
+
attr_accessor :binder, :leak_stack_on_error, :early_hints
|
86
91
|
|
87
|
-
|
88
|
-
forward :add_ssl_listener, :@binder
|
89
|
-
forward :add_unix_listener, :@binder
|
90
|
-
forward :connected_port, :@binder
|
92
|
+
def_delegators :@binder, :add_tcp_listener, :add_ssl_listener, :add_unix_listener, :connected_port
|
91
93
|
|
92
94
|
def inherit_binder(bind)
|
93
95
|
@binder = bind
|
94
|
-
@own_binder = false
|
95
96
|
end
|
96
97
|
|
97
98
|
def tcp_mode!
|
@@ -111,6 +112,7 @@ module Puma
|
|
111
112
|
begin
|
112
113
|
socket.setsockopt(6, 3, 1) if socket.kind_of? TCPSocket
|
113
114
|
rescue IOError, SystemCallError
|
115
|
+
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
114
116
|
end
|
115
117
|
end
|
116
118
|
|
@@ -118,6 +120,7 @@ module Puma
|
|
118
120
|
begin
|
119
121
|
socket.setsockopt(6, 3, 0) if socket.kind_of? TCPSocket
|
120
122
|
rescue IOError, SystemCallError
|
123
|
+
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
121
124
|
end
|
122
125
|
end
|
123
126
|
|
@@ -128,6 +131,7 @@ module Puma
|
|
128
131
|
begin
|
129
132
|
tcp_info = socket.getsockopt(Socket::SOL_TCP, Socket::TCP_INFO)
|
130
133
|
rescue IOError, SystemCallError
|
134
|
+
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
131
135
|
@precheck_closing = false
|
132
136
|
false
|
133
137
|
else
|
@@ -156,6 +160,18 @@ module Puma
|
|
156
160
|
@thread_pool and @thread_pool.spawned
|
157
161
|
end
|
158
162
|
|
163
|
+
|
164
|
+
# This number represents the number of requests that
|
165
|
+
# the server is capable of taking right now.
|
166
|
+
#
|
167
|
+
# For example if the number is 5 then it means
|
168
|
+
# there are 5 threads sitting idle ready to take
|
169
|
+
# a request. If one request comes in, then the
|
170
|
+
# value would be 4 until it finishes processing.
|
171
|
+
def pool_capacity
|
172
|
+
@thread_pool and @thread_pool.pool_capacity
|
173
|
+
end
|
174
|
+
|
159
175
|
# Lopez Mode == raw tcp apps
|
160
176
|
|
161
177
|
def run_lopez_mode(background=true)
|
@@ -188,7 +204,10 @@ module Puma
|
|
188
204
|
@events.fire :state, :running
|
189
205
|
|
190
206
|
if background
|
191
|
-
@thread = Thread.new
|
207
|
+
@thread = Thread.new do
|
208
|
+
Puma.set_thread_name "server"
|
209
|
+
handle_servers_lopez_mode
|
210
|
+
end
|
192
211
|
return @thread
|
193
212
|
else
|
194
213
|
handle_servers_lopez_mode
|
@@ -217,7 +236,11 @@ module Puma
|
|
217
236
|
# nothing
|
218
237
|
rescue Errno::ECONNABORTED
|
219
238
|
# client closed the socket even before accept
|
220
|
-
|
239
|
+
begin
|
240
|
+
io.close
|
241
|
+
rescue
|
242
|
+
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
243
|
+
end
|
221
244
|
end
|
222
245
|
end
|
223
246
|
end
|
@@ -234,11 +257,17 @@ module Puma
|
|
234
257
|
STDERR.puts "Exception handling servers: #{e.message} (#{e.class})"
|
235
258
|
STDERR.puts e.backtrace
|
236
259
|
ensure
|
237
|
-
|
238
|
-
|
260
|
+
begin
|
261
|
+
@check.close
|
262
|
+
rescue
|
263
|
+
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
264
|
+
end
|
239
265
|
|
240
|
-
|
241
|
-
|
266
|
+
# Prevent can't modify frozen IOError (RuntimeError)
|
267
|
+
begin
|
268
|
+
@notify.close
|
269
|
+
rescue IOError
|
270
|
+
# no biggy
|
242
271
|
end
|
243
272
|
end
|
244
273
|
|
@@ -288,11 +317,11 @@ module Puma
|
|
288
317
|
|
289
318
|
@events.ssl_error self, addr, cert, e
|
290
319
|
rescue HttpParserError => e
|
291
|
-
client.
|
320
|
+
client.write_error(400)
|
292
321
|
client.close
|
293
322
|
|
294
323
|
@events.parse_error self, client.env, e
|
295
|
-
rescue ConnectionError
|
324
|
+
rescue ConnectionError, EOFError
|
296
325
|
client.close
|
297
326
|
else
|
298
327
|
if process_now
|
@@ -322,7 +351,10 @@ module Puma
|
|
322
351
|
@events.fire :state, :running
|
323
352
|
|
324
353
|
if background
|
325
|
-
@thread = Thread.new
|
354
|
+
@thread = Thread.new do
|
355
|
+
Puma.set_thread_name "server"
|
356
|
+
handle_servers
|
357
|
+
end
|
326
358
|
return @thread
|
327
359
|
else
|
328
360
|
handle_servers
|
@@ -363,13 +395,20 @@ module Puma
|
|
363
395
|
end
|
364
396
|
|
365
397
|
pool << client
|
366
|
-
pool.wait_until_not_full
|
398
|
+
busy_threads = pool.wait_until_not_full
|
399
|
+
if busy_threads == 0
|
400
|
+
@options[:out_of_band].each(&:call) if @options[:out_of_band]
|
401
|
+
end
|
367
402
|
end
|
368
403
|
rescue SystemCallError
|
369
404
|
# nothing
|
370
405
|
rescue Errno::ECONNABORTED
|
371
406
|
# client closed the socket even before accept
|
372
|
-
|
407
|
+
begin
|
408
|
+
io.close
|
409
|
+
rescue
|
410
|
+
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
411
|
+
end
|
373
412
|
end
|
374
413
|
end
|
375
414
|
end
|
@@ -391,10 +430,6 @@ module Puma
|
|
391
430
|
ensure
|
392
431
|
@check.close
|
393
432
|
@notify.close
|
394
|
-
|
395
|
-
if @status != :restart and @own_binder
|
396
|
-
@binder.close
|
397
|
-
end
|
398
433
|
end
|
399
434
|
|
400
435
|
@events.fire :state, :done
|
@@ -431,6 +466,8 @@ module Puma
|
|
431
466
|
clean_thread_locals = @options[:clean_thread_locals]
|
432
467
|
close_socket = true
|
433
468
|
|
469
|
+
requests = 0
|
470
|
+
|
434
471
|
while true
|
435
472
|
case handle_request(client, buffer)
|
436
473
|
when false
|
@@ -444,7 +481,19 @@ module Puma
|
|
444
481
|
|
445
482
|
ThreadPool.clean_thread_locals if clean_thread_locals
|
446
483
|
|
447
|
-
|
484
|
+
requests += 1
|
485
|
+
|
486
|
+
check_for_more_data = @status == :run
|
487
|
+
|
488
|
+
if requests >= MAX_FAST_INLINE
|
489
|
+
# This will mean that reset will only try to use the data it already
|
490
|
+
# has buffered and won't try to read more data. What this means is that
|
491
|
+
# every client, independent of their request speed, gets treated like a slow
|
492
|
+
# one once every MAX_FAST_INLINE requests.
|
493
|
+
check_for_more_data = false
|
494
|
+
end
|
495
|
+
|
496
|
+
unless client.reset(check_for_more_data)
|
448
497
|
close_socket = false
|
449
498
|
client.set_timeout @persistent_timeout
|
450
499
|
@reactor.add client
|
@@ -473,7 +522,7 @@ module Puma
|
|
473
522
|
rescue HttpParserError => e
|
474
523
|
lowlevel_error(e, client.env)
|
475
524
|
|
476
|
-
client.
|
525
|
+
client.write_error(400)
|
477
526
|
|
478
527
|
@events.parse_error self, client.env, e
|
479
528
|
|
@@ -481,7 +530,7 @@ module Puma
|
|
481
530
|
rescue StandardError => e
|
482
531
|
lowlevel_error(e, client.env)
|
483
532
|
|
484
|
-
client.
|
533
|
+
client.write_error(500)
|
485
534
|
|
486
535
|
@events.unknown_error self, e, "Read"
|
487
536
|
|
@@ -491,6 +540,7 @@ module Puma
|
|
491
540
|
begin
|
492
541
|
client.close if close_socket
|
493
542
|
rescue IOError, SystemCallError
|
543
|
+
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
494
544
|
# Already closed
|
495
545
|
rescue StandardError => e
|
496
546
|
@events.unknown_error self, e, "Client"
|
@@ -555,19 +605,26 @@ module Puma
|
|
555
605
|
end
|
556
606
|
|
557
607
|
def default_server_port(env)
|
558
|
-
|
559
|
-
|
608
|
+
if ['on', HTTPS].include?(env[HTTPS_KEY]) || env[HTTP_X_FORWARDED_PROTO].to_s[0...5] == HTTPS || env[HTTP_X_FORWARDED_SCHEME] == HTTPS || env[HTTP_X_FORWARDED_SSL] == "on"
|
609
|
+
PORT_443
|
610
|
+
else
|
611
|
+
PORT_80
|
612
|
+
end
|
560
613
|
end
|
561
614
|
|
562
|
-
#
|
563
|
-
#
|
564
|
-
# the rack app. Then construct the response and write it back to
|
565
|
-
# +client+
|
615
|
+
# Takes the request +req+, invokes the Rack application to construct
|
616
|
+
# the response and writes it back to +req.io+.
|
566
617
|
#
|
567
|
-
# +
|
568
|
-
#
|
569
|
-
# it up again.
|
618
|
+
# The second parameter +lines+ is a IO-like object unique to this thread.
|
619
|
+
# This is normally an instance of Puma::IOBuffer.
|
570
620
|
#
|
621
|
+
# It'll return +false+ when the connection is closed, this doesn't mean
|
622
|
+
# that the response wasn't successful.
|
623
|
+
#
|
624
|
+
# It'll return +:async+ if the connection remains open but will be handled
|
625
|
+
# elsewhere, i.e. the connection has been hijacked by the Rack application.
|
626
|
+
#
|
627
|
+
# Finally, it'll return +true+ on keep-alive connections.
|
571
628
|
def handle_request(req, lines)
|
572
629
|
env = req.env
|
573
630
|
client = req.io
|
@@ -590,7 +647,29 @@ module Puma
|
|
590
647
|
head = env[REQUEST_METHOD] == HEAD
|
591
648
|
|
592
649
|
env[RACK_INPUT] = body
|
593
|
-
env[RACK_URL_SCHEME] =
|
650
|
+
env[RACK_URL_SCHEME] = default_server_port(env) == PORT_443 ? HTTPS : HTTP
|
651
|
+
|
652
|
+
if @early_hints
|
653
|
+
env[EARLY_HINTS] = lambda { |headers|
|
654
|
+
begin
|
655
|
+
fast_write client, "HTTP/1.1 103 Early Hints\r\n".freeze
|
656
|
+
|
657
|
+
headers.each_pair do |k, vs|
|
658
|
+
if vs.respond_to?(:to_s) && !vs.to_s.empty?
|
659
|
+
vs.to_s.split(NEWLINE).each do |v|
|
660
|
+
fast_write client, "#{k}: #{v}\r\n"
|
661
|
+
end
|
662
|
+
else
|
663
|
+
fast_write client, "#{k}: #{vs}\r\n"
|
664
|
+
end
|
665
|
+
end
|
666
|
+
|
667
|
+
fast_write client, "\r\n".freeze
|
668
|
+
rescue ConnectionError
|
669
|
+
# noop, if we lost the socket we just won't send the early hints
|
670
|
+
end
|
671
|
+
}
|
672
|
+
end
|
594
673
|
|
595
674
|
# A rack extension. If the app writes #call'ables to this
|
596
675
|
# array, we will invoke them when the request is done.
|
@@ -733,8 +812,8 @@ module Puma
|
|
733
812
|
|
734
813
|
begin
|
735
814
|
res_body.each do |part|
|
815
|
+
next if part.bytesize.zero?
|
736
816
|
if chunked
|
737
|
-
next if part.bytesize.zero?
|
738
817
|
fast_write client, part.bytesize.to_s(16)
|
739
818
|
fast_write client, line_ending
|
740
819
|
fast_write client, part
|
@@ -884,6 +963,10 @@ module Puma
|
|
884
963
|
@events.debug "Drained #{count} additional connections."
|
885
964
|
end
|
886
965
|
|
966
|
+
if @status != :restart
|
967
|
+
@binder.close
|
968
|
+
end
|
969
|
+
|
887
970
|
if @thread_pool
|
888
971
|
if timeout = @options[:force_shutdown_after]
|
889
972
|
@thread_pool.shutdown timeout.to_i
|
@@ -893,35 +976,38 @@ module Puma
|
|
893
976
|
end
|
894
977
|
end
|
895
978
|
|
896
|
-
|
897
|
-
# off the request queue before finally exiting.
|
898
|
-
#
|
899
|
-
def stop(sync=false)
|
979
|
+
def notify_safely(message)
|
900
980
|
begin
|
901
|
-
@notify <<
|
981
|
+
@notify << message
|
902
982
|
rescue IOError
|
903
|
-
|
983
|
+
# The server, in another thread, is shutting down
|
984
|
+
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
985
|
+
rescue RuntimeError => e
|
986
|
+
# Temporary workaround for https://bugs.ruby-lang.org/issues/13239
|
987
|
+
if e.message.include?('IOError')
|
988
|
+
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
989
|
+
else
|
990
|
+
raise e
|
991
|
+
end
|
904
992
|
end
|
993
|
+
end
|
994
|
+
private :notify_safely
|
905
995
|
|
996
|
+
# Stops the acceptor thread and then causes the worker threads to finish
|
997
|
+
# off the request queue before finally exiting.
|
998
|
+
|
999
|
+
def stop(sync=false)
|
1000
|
+
notify_safely(STOP_COMMAND)
|
906
1001
|
@thread.join if @thread && sync
|
907
1002
|
end
|
908
1003
|
|
909
1004
|
def halt(sync=false)
|
910
|
-
|
911
|
-
@notify << HALT_COMMAND
|
912
|
-
rescue IOError
|
913
|
-
# The server, in another thread, is shutting down
|
914
|
-
end
|
915
|
-
|
1005
|
+
notify_safely(HALT_COMMAND)
|
916
1006
|
@thread.join if @thread && sync
|
917
1007
|
end
|
918
1008
|
|
919
1009
|
def begin_restart
|
920
|
-
|
921
|
-
@notify << RESTART_COMMAND
|
922
|
-
rescue IOError
|
923
|
-
# The server, in another thread, is shutting down
|
924
|
-
end
|
1010
|
+
notify_safely(RESTART_COMMAND)
|
925
1011
|
end
|
926
1012
|
|
927
1013
|
def fast_write(io, str)
|