riser 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,869 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ require 'io/wait'
4
+ require 'socket'
5
+ require 'uri'
6
+
7
+ module Riser
8
+ class TimeoutSizedQueue
9
+ def initialize(size, name: nil)
10
+ @size = size
11
+ @queue = []
12
+ @closed = false
13
+ @mutex = Thread::Mutex.new
14
+ @push_cond = Thread::ConditionVariable.new
15
+ @pop_cond = Thread::ConditionVariable.new
16
+ @name = name && name.dup.freeze
17
+ @stat_enable = false
18
+ end
19
+
20
+ def size
21
+ @mutex.synchronize{ @queue.size }
22
+ end
23
+
24
+ alias length size
25
+
26
+ def empty?
27
+ @mutex.synchronize{ @queue.empty? }
28
+ end
29
+
30
+ def closed?
31
+ @mutex.synchronize{ @closed }
32
+ end
33
+
34
+ def close
35
+ @mutex.synchronize{
36
+ @closed = true
37
+ @pop_cond.broadcast
38
+ }
39
+ nil
40
+ end
41
+
42
+ def at_end_of_queue?
43
+ @mutex.synchronize{ @closed && @queue.empty? }
44
+ end
45
+
46
+ def push(value, timeout_seconds)
47
+ @mutex.synchronize{
48
+ @closed and raise 'closed'
49
+ if (@stat_enable) then
50
+ @stat_push_count += 1
51
+ @stat_push_average_queue_size = (@stat_push_average_queue_size * (@stat_push_count - 1) + @queue.size) / @stat_push_count
52
+ end
53
+ unless (@queue.size < @size) then
54
+ @stat_push_wait_count += 1 if @stat_enable
55
+ @push_cond.wait(@mutex, timeout_seconds)
56
+ unless (@queue.size < @size) then
57
+ @stat_push_timeout_count += 1 if @stat_enable
58
+ return
59
+ end
60
+ end
61
+ @pop_cond.signal
62
+ @queue.push(value)
63
+ self
64
+ }
65
+ end
66
+
67
+ def pop
68
+ @mutex.synchronize{
69
+ if (@stat_enable) then
70
+ @stat_pop_count += 1
71
+ @stat_pop_average_queue_size = (@stat_pop_average_queue_size * (@stat_pop_count - 1) + @queue.size) / @stat_pop_count
72
+ end
73
+ while (@queue.empty?)
74
+ @closed and return
75
+ @stat_pop_wait_count += 1 if @stat_enable
76
+ @pop_cond.wait(@mutex)
77
+ end
78
+ @push_cond.signal
79
+ @queue.shift
80
+ }
81
+ end
82
+
83
+ def stat_reset_no_lock
84
+ @stat_start_time = Time.now
85
+ @stat_push_average_queue_size = 0.0
86
+ @stat_push_count = 0
87
+ @stat_push_wait_count = 0
88
+ @stat_push_timeout_count = 0
89
+ @stat_pop_average_queue_size = 0.0
90
+ @stat_pop_count = 0
91
+ @stat_pop_wait_count = 0
92
+ end
93
+ private :stat_reset_no_lock
94
+
95
+ def stat_start
96
+ @mutex.synchronize{
97
+ unless (@stat_enable) then
98
+ stat_reset_no_lock
99
+ @stat_enable = true
100
+ end
101
+ }
102
+ nil
103
+ end
104
+
105
+ def stat_stop
106
+ @mutex.synchronize{
107
+ @stat_enable = false
108
+ }
109
+ nil
110
+ end
111
+
112
+ def stat_get(reset: true)
113
+ if (@stat_enable) then
114
+ info = nil
115
+ @mutex.synchronize{
116
+ info = {
117
+ queue_name: @name,
118
+ queue_size: @size,
119
+ closed: @closed,
120
+ start_time: @stat_start_time,
121
+ push_average_queue_size: @stat_push_average_queue_size,
122
+ push_count: @stat_push_count,
123
+ push_wait_count: @stat_push_wait_count,
124
+ push_timeout_count: @stat_push_timeout_count,
125
+ pop_average_queue_size: @stat_pop_average_queue_size,
126
+ pop_count: @stat_pop_count,
127
+ pop_wait_count: @stat_pop_wait_count
128
+ }
129
+
130
+ if (reset) then
131
+ stat_reset_no_lock
132
+ end
133
+ }
134
+
135
+ info[:get_time] = Time.now
136
+ info[:elapsed_seconds] = info[:get_time] - info[:start_time]
137
+ info[:push_wait_ratio] = info[:push_wait_count].to_f / info[:push_count]
138
+ info[:push_timeout_ratio] = info[:push_timeout_count].to_f / info[:push_count]
139
+ info[:pop_wait_ratio] = info[:pop_wait_count].to_f / info[:pop_count]
140
+
141
+ # sort
142
+ [ :queue_name,
143
+ :queue_size,
144
+ :closed,
145
+ :start_time,
146
+ :get_time,
147
+ :elapsed_seconds,
148
+ :push_average_queue_size,
149
+ :push_count,
150
+ :push_wait_count,
151
+ :push_wait_ratio,
152
+ :push_timeout_count,
153
+ :push_timeout_ratio,
154
+ :pop_average_queue_size,
155
+ :pop_count,
156
+ :pop_wait_count,
157
+ :pop_wait_ratio
158
+ ].each do |name|
159
+ info[name] = info.delete(name)
160
+ end
161
+
162
+ info
163
+ end
164
+ end
165
+ end
166
+
167
+ class SocketAddress
168
+ def initialize(type)
169
+ @type = type
170
+ end
171
+
172
+ attr_reader :type
173
+
174
+ def to_a
175
+ [ @type ]
176
+ end
177
+
178
+ def to_s
179
+ to_a.map{|s|
180
+ if (s.to_s.include? ':') then
181
+ "[#{s}]"
182
+ else
183
+ s
184
+ end
185
+ }.join(':')
186
+ end
187
+
188
+ def ==(other)
189
+ if (other.is_a? SocketAddress) then
190
+ self.to_a == other.to_a
191
+ else
192
+ false
193
+ end
194
+ end
195
+
196
+ def eql?(other)
197
+ self == other
198
+ end
199
+
200
+ def hash
201
+ to_a.hash ^ self.class.hash
202
+ end
203
+
204
+ def self.parse(config)
205
+ unsquare = proc{|s| s.sub(/\A \[/x, '').sub(/\] \z/x, '') }
206
+ case (config)
207
+ when String
208
+ case (config)
209
+ when /\A tcp:/x
210
+ uri = URI(config)
211
+ if (uri.host && uri.port) then
212
+ return TCPSocketAddress.new(unsquare.call(uri.host), uri.port)
213
+ end
214
+ when /\A unix:/x
215
+ uri = URI(config)
216
+ if (uri.path && ! uri.path.empty?) then
217
+ return UNIXSocketAddress.new(uri.path)
218
+ end
219
+ when %r"\A [A-Za-z]+:/"x
220
+ # unknown URI scheme
221
+ when /\A (\S+):(\d+) \z/x
222
+ host = $1
223
+ port = $2.to_i
224
+ return TCPSocketAddress.new(unsquare.call(host), port)
225
+ end
226
+ when Hash
227
+ if (type = config[:type] || config['type']) then
228
+ case (type.to_s)
229
+ when 'tcp'
230
+ host = config[:host] || config['host']
231
+ port = config[:port] || config['port']
232
+ if (host && (host.is_a? String) && port && (port.is_a? Integer)) then
233
+ return TCPSocketAddress.new(unsquare.call(host), port)
234
+ end
235
+ when 'unix'
236
+ path = config[:path] || config['path']
237
+ if (path && (path.is_a? String) && ! path.empty?) then
238
+ return UNIXSocketAddress.new(path)
239
+ end
240
+ end
241
+ end
242
+ end
243
+
244
+ return
245
+ end
246
+ end
247
+
248
+ class TCPSocketAddress < SocketAddress
249
+ def initialize(host, port)
250
+ super(:tcp)
251
+ @host = host
252
+ @port = port
253
+ end
254
+
255
+ attr_reader :host
256
+ attr_reader :port
257
+
258
+ def to_a
259
+ super << @host << @port
260
+ end
261
+
262
+ def open_server
263
+ TCPServer.new(@host, @port)
264
+ end
265
+ end
266
+
267
+ class UNIXSocketAddress < SocketAddress
268
+ def initialize(path)
269
+ super(:unix)
270
+ @path = path
271
+ end
272
+
273
+ attr_reader :path
274
+
275
+ def to_a
276
+ super << @path
277
+ end
278
+
279
+ def open_server
280
+ UNIXServer.new(@path)
281
+ end
282
+ end
283
+
284
+ module ServerSignal
285
+ SIGNAL_STOP_GRACEFUL = :TERM
286
+ SIGNAL_STOP_FORCED = :INT
287
+ SIGNAL_STAT_GET_AND_RESET = :USR1
288
+ SIGNAL_STAT_GET_NO_RESET = :USR2
289
+ SIGNAL_STAT_STOP = :WINCH
290
+ SIGNAL_RESTART_GRACEFUL = :HUP
291
+ SIGNAL_RESTART_FORCED = :QUIT
292
+ end
293
+
294
+ class SocketThreadDispatcher
295
+ def initialize(thread_queue_name)
296
+ @thread_num = nil
297
+ @thread_queue_name = thread_queue_name
298
+ @thread_queue_size = nil
299
+ @thread_queue_polling_timeout_seconds = nil
300
+ @at_stop = nil
301
+ @at_stat = nil
302
+ @at_stat_get = nil
303
+ @at_stat_stop = nil
304
+ @preprocess = nil
305
+ @postprocess = nil
306
+ @accept = nil
307
+ @accept_return = nil
308
+ @dispatch = nil
309
+ @stop_state = nil
310
+ @stat_operation_queue = []
311
+ end
312
+
313
+ attr_accessor :thread_num
314
+ attr_accessor :thread_queue_size
315
+ attr_accessor :thread_queue_polling_timeout_seconds
316
+
317
+ def at_stop(&block) # :yields: stop_state
318
+ @at_stop = block
319
+ nil
320
+ end
321
+
322
+ def at_stat(&block) # :yields: stat_info
323
+ @at_stat = block
324
+ nil
325
+ end
326
+
327
+ def at_stat_get(&block) # :yields: reset
328
+ @at_stat_get = block
329
+ nil
330
+ end
331
+
332
+ def at_stat_stop(&block) # :yields:
333
+ @at_stat_stop = block
334
+ nil
335
+ end
336
+
337
+ def preprocess(&block) # :yields:
338
+ @preprocess = block
339
+ nil
340
+ end
341
+
342
+ def postprocess(&block) # :yields:
343
+ @postprocess = block
344
+ nil
345
+ end
346
+
347
+ def accept(&block) # :yields:
348
+ @accept = block
349
+ nil
350
+ end
351
+
352
+ def accept_return(&block) # :yields:
353
+ @accept_return = block
354
+ nil
355
+ end
356
+
357
+ def dispatch(&block) # :yields: socket
358
+ @dispatch = block
359
+ nil
360
+ end
361
+
362
+ # should be called from signal(2) handler
363
+ def signal_stop_graceful
364
+ @stop_state ||= :graceful
365
+ nil
366
+ end
367
+
368
+ # should be called from signal(2) handler
369
+ def signal_stop_forced
370
+ @stop_state ||= :forced
371
+ nil
372
+ end
373
+
374
+ # should be called from signal(2) handler
375
+ def signal_stat_get(reset: true)
376
+ if (reset) then
377
+ @stat_operation_queue << :get_and_reset
378
+ else
379
+ @stat_operation_queue << :get
380
+ end
381
+
382
+ nil
383
+ end
384
+
385
+ # should be called from signal(2) handler
386
+ def signal_stat_stop
387
+ @stat_operation_queue << :stop
388
+ nil
389
+ end
390
+
391
+ def apply_signal_stat(queue)
392
+ unless (@stat_operation_queue.empty?) then
393
+ while (stat_ope = @stat_operation_queue.shift)
394
+ case (stat_ope)
395
+ when :get_and_reset
396
+ queue.stat_start
397
+ @at_stat.call(queue.stat_get(reset: true))
398
+ @at_stat_get.call(true)
399
+ when :get
400
+ queue.stat_start
401
+ @at_stat.call(queue.stat_get(reset: false))
402
+ @at_stat_get.call(false)
403
+ when :stop
404
+ queue.stat_stop
405
+ @at_stat_stop.call
406
+ else
407
+ raise "internal error: unknown stat operation <#{stat_ope.inspect}>"
408
+ end
409
+ end
410
+ end
411
+ end
412
+ private :apply_signal_stat
413
+
414
+ # should be executed on the main thread sharing the stack with
415
+ # signal(2) handlers
416
+ #
417
+ # _server_socket is a dummy argument to call like
418
+ # SocketProcessDispatcher#start.
419
+ def start(_server_socket=nil)
420
+ @preprocess.call
421
+ begin
422
+ queue = TimeoutSizedQueue.new(@thread_queue_size, name: @thread_queue_name)
423
+ begin
424
+ thread_list = []
425
+ @thread_num.times{|i|
426
+ thread_list << Thread.new{
427
+ Thread.current[:number] = i
428
+ while (socket = queue.pop)
429
+ begin
430
+ @dispatch.call(socket)
431
+ ensure
432
+ socket.close unless socket.closed?
433
+ end
434
+ end
435
+ }
436
+ }
437
+
438
+ catch (:end_of_server) {
439
+ while (true)
440
+ begin
441
+ @stop_state and throw(:end_of_server)
442
+ apply_signal_stat(queue)
443
+ socket = @accept.call
444
+ end until (socket)
445
+
446
+ until (queue.push(socket, @thread_queue_polling_timeout_seconds))
447
+ if (@stop_state == :forced) then
448
+ socket.close
449
+ @accept_return.call
450
+ throw(:end_of_server)
451
+ end
452
+ apply_signal_stat(queue)
453
+ end
454
+ @accept_return.call
455
+ end
456
+ }
457
+ ensure
458
+ queue.close
459
+ end
460
+
461
+ @at_stop.call(@stop_state)
462
+ case (@stop_state)
463
+ when :graceful
464
+ for thread in thread_list
465
+ thread.join
466
+ end
467
+ when :forced
468
+ for thread in thread_list
469
+ thread.kill
470
+ end
471
+ else
472
+ raise "internal error: unknown stop state <#{@stop_state.inspect}>"
473
+ end
474
+ ensure
475
+ @postprocess.call
476
+ end
477
+
478
+ nil
479
+ end
480
+ end
481
+
482
+ SocketProcess = Struct.new(:pid, :io) # :nodoc:
483
+
484
+ class SocketProcessDispatcher
485
+ include ServerSignal
486
+
487
+ NO_CALL = proc{} # :nodoc:
488
+
489
+ def initialize(process_queue_name, thread_queue_name)
490
+ @accept_polling_timeout_seconds = nil
491
+ @process_num = nil
492
+ @process_queue_name = process_queue_name
493
+ @process_queue_size = nil
494
+ @process_queue_polling_timeout_seconds = nil
495
+ @process_send_io_polling_timeout_seconds = nil
496
+ @thread_num = nil
497
+ @thread_queue_name = thread_queue_name
498
+ @thread_queue_size = nil
499
+ @thread_queue_polling_timeout_seconds = nil
500
+ @at_fork= nil
501
+ @at_stop = nil
502
+ @at_stat = nil
503
+ @preprocess = nil
504
+ @postprocess = nil
505
+ @dispatch = nil
506
+ @process_dispatcher = nil
507
+ end
508
+
509
+ attr_accessor :accept_polling_timeout_seconds
510
+ attr_accessor :process_num
511
+ attr_accessor :process_queue_size
512
+ attr_accessor :process_queue_polling_timeout_seconds
513
+ attr_accessor :process_send_io_polling_timeout_seconds
514
+ attr_accessor :thread_num
515
+ attr_accessor :thread_queue_size
516
+ attr_accessor :thread_queue_polling_timeout_seconds
517
+
518
+ def at_fork(&block) # :yields:
519
+ @at_fork = block
520
+ nil
521
+ end
522
+
523
+ def at_stop(&block) # :yields: stop_state
524
+ @at_stop = block
525
+ nil
526
+ end
527
+
528
+ def at_stat(&block) # :yields: stat_info
529
+ @at_stat = block
530
+ nil
531
+ end
532
+
533
+ def preprocess(&block) # :yields:
534
+ @preprocess = block
535
+ nil
536
+ end
537
+
538
+ def postprocess(&block) # :yields:
539
+ @postprocess = block
540
+ nil
541
+ end
542
+
543
+ def dispatch(&block) # :yields: accept_object
544
+ @dispatch = block
545
+ nil
546
+ end
547
+
548
+ # should be called from signal(2) handler
549
+ def signal_stop_graceful
550
+ @process_dispatcher.signal_stop_graceful if @process_dispatcher
551
+ nil
552
+ end
553
+
554
+ # should be called from signal(2) handler
555
+ def signal_stop_forced
556
+ @process_dispatcher.signal_stop_forced if @process_dispatcher
557
+ nil
558
+ end
559
+
560
+ # should be called from signal(2) handler
561
+ def signal_stat_get(reset: true)
562
+ @process_dispatcher.signal_stat_get(reset: reset) if @process_dispatcher
563
+ nil
564
+ end
565
+
566
+ # should be called from signal(2) handler
567
+ def signal_stat_stop
568
+ @process_dispatcher.signal_stat_stop if @process_dispatcher
569
+ nil
570
+ end
571
+
572
+ # after this method call is completed, the object will be ready to
573
+ # accept `signal_...' methods.
574
+ def setup
575
+ @process_dispatcher = SocketThreadDispatcher.new(@process_queue_name)
576
+ nil
577
+ end
578
+
579
+ def start(server_socket)
580
+ case (server_socket)
581
+ when TCPServer, UNIXServer
582
+ socket_class = server_socket.class.superclass
583
+ else
584
+ socket_class = IO
585
+ end
586
+
587
+ process_list = []
588
+ @process_num.times do |pos|
589
+ child_io, parent_io = UNIXSocket.socketpair
590
+ pid = Process.fork{
591
+ parent_io.close
592
+ pos.times do |i|
593
+ process_list[i].io.close
594
+ end
595
+
596
+ thread_dispatcher = SocketThreadDispatcher.new("#{@thread_queue_name}-#{pos}")
597
+ thread_dispatcher.thread_num = @thread_num
598
+ thread_dispatcher.thread_queue_size = @thread_queue_size
599
+ thread_dispatcher.thread_queue_polling_timeout_seconds = @thread_queue_polling_timeout_seconds
600
+
601
+ thread_dispatcher.at_stop(&@at_stop)
602
+ thread_dispatcher.at_stat(&@at_stat)
603
+ thread_dispatcher.at_stat_get(&NO_CALL)
604
+ thread_dispatcher.at_stat_stop(&NO_CALL)
605
+ thread_dispatcher.preprocess(&@preprocess)
606
+ thread_dispatcher.postprocess(&@postprocess)
607
+
608
+ thread_dispatcher.accept{
609
+ if (child_io.wait_readable(@process_send_io_polling_timeout_seconds) != nil) then
610
+ command = child_io.read(5)
611
+ command == "SEND\n" or raise "internal error: unknown command <#{command.inspect}>"
612
+ child_io.recv_io(socket_class)
613
+ end
614
+ }
615
+ thread_dispatcher.accept_return{ child_io.write("RADY\n") }
616
+ thread_dispatcher.dispatch(&@dispatch)
617
+
618
+ Signal.trap(SIGNAL_STOP_GRACEFUL) { thread_dispatcher.signal_stop_graceful }
619
+ Signal.trap(SIGNAL_STOP_FORCED) { thread_dispatcher.signal_stop_forced }
620
+ Signal.trap(SIGNAL_STAT_GET_AND_RESET) { thread_dispatcher.signal_stat_get(reset: true) }
621
+ Signal.trap(SIGNAL_STAT_GET_NO_RESET) { thread_dispatcher.signal_stat_get(reset: false) }
622
+ Signal.trap(SIGNAL_STAT_STOP) { thread_dispatcher.signal_stat_stop }
623
+
624
+ begin
625
+ child_io.write("RADY\n")
626
+ @at_fork.call
627
+ thread_dispatcher.start
628
+ ensure
629
+ child_io.close
630
+ end
631
+ }
632
+ child_io.close
633
+
634
+ process_list << SocketProcess.new(pid, parent_io)
635
+ end
636
+
637
+ for process in process_list
638
+ response = process.io.read(5)
639
+ response == "RADY\n" or raise "internal error: unknown response <#{response.inspect}>"
640
+ end
641
+
642
+ setup unless @process_dispatcher
643
+ @process_dispatcher.thread_num = @process_num
644
+ @process_dispatcher.thread_queue_size = @process_queue_size
645
+ @process_dispatcher.thread_queue_polling_timeout_seconds = @process_queue_polling_timeout_seconds
646
+
647
+ @process_dispatcher.at_stop{|state|
648
+ case (state)
649
+ when :graceful
650
+ for process in process_list
651
+ Process.kill(SIGNAL_STOP_GRACEFUL, process.pid)
652
+ end
653
+ when :forced
654
+ for process in process_list
655
+ Process.kill(SIGNAL_STOP_FORCED, process.pid)
656
+ end
657
+ end
658
+ }
659
+ @process_dispatcher.at_stat(&@at_stat)
660
+ @process_dispatcher.at_stat_get{|reset|
661
+ if (reset) then
662
+ for process in process_list
663
+ Process.kill(SIGNAL_STAT_GET_AND_RESET, process.pid)
664
+ end
665
+ else
666
+ for process in process_list
667
+ Process.kill(SIGNAL_STAT_GET_NO_RESET, process.pid)
668
+ end
669
+ end
670
+ }
671
+ @process_dispatcher.at_stat_stop{
672
+ for process in process_list
673
+ Process.kill(SIGNAL_STAT_STOP, process.pid)
674
+ end
675
+ }
676
+ @process_dispatcher.preprocess(&NO_CALL)
677
+ @process_dispatcher.postprocess(&NO_CALL)
678
+
679
+ @process_dispatcher.accept{
680
+ if (server_socket.wait_readable(@accept_polling_timeout_seconds) != nil) then
681
+ server_socket.accept
682
+ end
683
+ }
684
+ @process_dispatcher.accept_return(&NO_CALL)
685
+ @process_dispatcher.dispatch{|socket|
686
+ process = process_list[Thread.current[:number]]
687
+ process.io.write("SEND\n")
688
+ process.io.send_io(socket)
689
+ response = process.io.read(5)
690
+ response == "RADY\n" or raise "internal error: unknown response <#{response.inspect}>"
691
+ }
692
+ @process_dispatcher.start
693
+
694
+ for process in process_list
695
+ Process.wait(process.pid)
696
+ process.io.close
697
+ end
698
+
699
+ nil
700
+ end
701
+ end
702
+
703
+ class SocketServer
704
+ NO_CALL = proc{} # :nodoc:
705
+
706
+ def initialize
707
+ @accept_polling_timeout_seconds = 0.1
708
+ @process_num = 0
709
+ @process_queue_size = 20
710
+ @process_queue_polling_timeout_seconds = 0.1
711
+ @process_send_io_polling_timeout_seconds = 0.1
712
+ @thread_num = 4
713
+ @thread_queue_size = 20
714
+ @thread_queue_polling_timeout_seconds = 0.1
715
+ @before_start = NO_CALL
716
+ @at_fork = NO_CALL
717
+ @at_stop = NO_CALL
718
+ @at_stat = NO_CALL
719
+ @preprocess = NO_CALL
720
+ @postprocess = NO_CALL
721
+ @after_stop = NO_CALL
722
+ @dispatch = nil
723
+ @dispatcher = nil
724
+ end
725
+
726
+ attr_accessor :accept_polling_timeout_seconds
727
+ attr_accessor :process_num
728
+ attr_accessor :process_queue_size
729
+ attr_accessor :process_queue_polling_timeout_seconds
730
+ attr_accessor :process_send_io_polling_timeout_seconds
731
+ attr_accessor :thread_num
732
+ attr_accessor :thread_queue_size
733
+ attr_accessor :thread_queue_polling_timeout_seconds
734
+
735
+ def before_start(&block) # :yields: server_socket
736
+ @before_start = block
737
+ nil
738
+ end
739
+
740
+ def at_fork(&block) # :yields:
741
+ @at_fork = block
742
+ nil
743
+ end
744
+
745
+ def at_stop(&block) # :yields: stop_state
746
+ @at_stop = block
747
+ nil
748
+ end
749
+
750
+ def at_stat(&block) # :yields: stat_info
751
+ @at_stat = block
752
+ nil
753
+ end
754
+
755
+ def preprocess(&block) # :yields:
756
+ @preprocess = block
757
+ nil
758
+ end
759
+
760
+ def postprocess(&block) # :yields:
761
+ @postprocess = block
762
+ nil
763
+ end
764
+
765
+ def after_stop(&block) # :yields:
766
+ @after_stop = block
767
+ nil
768
+ end
769
+
770
+ def dispatch(&block) # :yields: socket
771
+ @dispatch = block
772
+ nil
773
+ end
774
+
775
+ # should be called from signal(2) handler
776
+ def signal_stop_graceful
777
+ @dispatcher.signal_stop_graceful if @dispatcher
778
+ nil
779
+ end
780
+
781
+ # should be called from signal(2) handler
782
+ def signal_stop_forced
783
+ @dispatcher.signal_stop_forced if @dispatcher
784
+ nil
785
+ end
786
+
787
+ # should be called from signal(2) handler
788
+ def signal_stat_get(reset: true)
789
+ @dispatcher.signal_stat_get(reset: reset) if @dispatcher
790
+ nil
791
+ end
792
+
793
+ # should be called from signal(2) handler
794
+ def signal_stat_stop
795
+ @dispatcher.signal_stat_stop if @dispatcher
796
+ nil
797
+ end
798
+
799
+ # after this method call is completed, the object will be ready to
800
+ # accept `signal_...' methods.
801
+ def setup(server_socket)
802
+ if (@process_num > 0) then
803
+ @dispatcher = SocketProcessDispatcher.new('process_queue', 'thread_queue')
804
+ @dispatcher.accept_polling_timeout_seconds = @accept_polling_timeout_seconds
805
+ @dispatcher.process_num = @process_num
806
+ @dispatcher.process_queue_size = @process_queue_size
807
+ @dispatcher.process_queue_polling_timeout_seconds = @process_queue_polling_timeout_seconds
808
+ @dispatcher.process_send_io_polling_timeout_seconds = @process_send_io_polling_timeout_seconds
809
+ @dispatcher.thread_num = @thread_num
810
+ @dispatcher.thread_queue_size = @thread_queue_size
811
+ @dispatcher.thread_queue_polling_timeout_seconds = @thread_queue_polling_timeout_seconds
812
+
813
+ @dispatcher.at_fork{
814
+ server_socket.close
815
+ @at_fork.call
816
+ }
817
+ @dispatcher.at_stop(&@at_stop)
818
+ @dispatcher.at_stat(&@at_stat)
819
+ @dispatcher.preprocess(&@preprocess)
820
+ @dispatcher.postprocess(&@postprocess)
821
+ @dispatcher.dispatch(&@dispatch)
822
+ @dispatcher.setup
823
+ else
824
+ @dispatcher = SocketThreadDispatcher.new('thread_queue')
825
+ @dispatcher.thread_num = @thread_num
826
+ @dispatcher.thread_queue_size = @thread_queue_size
827
+ @dispatcher.thread_queue_polling_timeout_seconds = @thread_queue_polling_timeout_seconds
828
+
829
+ @dispatcher.at_stop(&@at_stop)
830
+ @dispatcher.at_stat(&@at_stat)
831
+ @dispatcher.at_stat_get(&NO_CALL)
832
+ @dispatcher.at_stat_stop(&NO_CALL)
833
+ @dispatcher.preprocess(&@preprocess)
834
+ @dispatcher.postprocess(&@postprocess)
835
+ @dispatcher.accept{
836
+ if (server_socket.wait_readable(@accept_polling_timeout_seconds) != nil) then
837
+ server_socket.accept
838
+ end
839
+ }
840
+ @dispatcher.accept_return(&NO_CALL)
841
+ @dispatcher.dispatch(&@dispatch)
842
+ end
843
+
844
+ nil
845
+ end
846
+
847
+ # should be executed on the main thread sharing the stack with
848
+ # signal(2) handlers
849
+ def start(server_socket)
850
+ unless (@dispatcher) then
851
+ setup(server_socket)
852
+ end
853
+
854
+ @before_start.call(server_socket)
855
+ begin
856
+ @dispatcher.start(server_socket)
857
+ ensure
858
+ @after_stop.call
859
+ end
860
+
861
+ nil
862
+ end
863
+ end
864
+ end
865
+
866
+ # Local Variables:
867
+ # mode: Ruby
868
+ # indent-tabs-mode: nil
869
+ # End: